void Context::RegisterAttribute(StringHash objectType, const AttributeInfo& attr) { // None or pointer types can not be supported if (attr.type_ == VAR_NONE || attr.type_ == VAR_VOIDPTR || attr.type_ == VAR_PTR) { URHO3D_LOGWARNING("Attempt to register unsupported attribute type " + Variant::GetTypeName(attr.type_) + " to class " + GetTypeName(objectType)); return; } attributes_[objectType].Push(attr); if (attr.mode_ & AM_NET) networkAttributes_[objectType].Push(attr); }
bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard) { if (start == 0 && count == vertexCount_) return SetData(data); if (!data) { URHO3D_LOGERROR("Null pointer for vertex buffer data"); return false; } if (!vertexSize_) { URHO3D_LOGERROR("Vertex elements not defined, can not set vertex buffer data"); return false; } if (start + count > vertexCount_) { URHO3D_LOGERROR("Illegal range for setting new vertex buffer data"); return false; } if (!count) return true; if (shadowData_ && shadowData_.Get() + start * vertexSize_ != data) memcpy(shadowData_.Get() + start * vertexSize_, data, count * vertexSize_); if (object_) { if (!graphics_->IsDeviceLost()) { graphics_->SetVBO(object_); if (!discard || start != 0) glBufferSubData(GL_ARRAY_BUFFER, start * vertexSize_, count * vertexSize_, data); else glBufferData(GL_ARRAY_BUFFER, count * vertexSize_, data, dynamic_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); } else { URHO3D_LOGWARNING("Vertex buffer data assignment while device is lost"); dataPending_ = true; } } return true; }
void Graphics::FreeScratchBuffer(void* buffer) { if (!buffer) return; for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i) { if (i->reserved_ && i->data_.Get() == buffer) { i->reserved_ = false; return; } } URHO3D_LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)(size_t)buffer) + " not found"); }
void Graphics::FreeScratchBuffer(void* buffer) { if (!buffer) return; for (ScratchBuffer & elem : scratchBuffers_) { if (elem.reserved_ && elem.data_.get() == buffer) { elem.reserved_ = false; return; } } URHO3D_LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)(size_t)buffer) + " not found"); }
bool SpriteSheet2D::EndLoadFromPListFile() { ResourceCache* cache = GetSubsystem<ResourceCache>(); texture_ = cache->GetResource<Texture2D>(loadTextureName_); if (!texture_) { URHO3D_LOGERROR("Could not load texture " + loadTextureName_); loadXMLFile_.Reset(); loadTextureName_.Clear(); return false; } const PListValueMap& root = loadPListFile_->GetRoot(); const PListValueMap& frames = root["frames"]->GetValueMap(); for (PListValueMap::ConstIterator i = frames.Begin(); i != frames.End(); ++i) { String name = i->first_.Split('.')[0]; const PListValueMap& frameInfo = i->second_.GetValueMap(); if (frameInfo["rotated"]->GetBool()) { URHO3D_LOGWARNING("Rotated sprite is not support now"); continue; } IntRect rectangle = frameInfo["frame"]->GetIntRect(); Vector2 hotSpot(0.5f, 0.5f); IntVector2 offset(0, 0); IntRect sourceColorRect = frameInfo["sourceColorRect"]->GetIntRect(); if (sourceColorRect.left_ != 0 && sourceColorRect.top_ != 0) { offset.x_ = -sourceColorRect.left_; offset.y_ = -sourceColorRect.top_; IntVector2 sourceSize = frameInfo["sourceSize"]->GetIntVector2(); hotSpot.x_ = ((float)offset.x_ + sourceSize.x_ / 2) / rectangle.Width(); hotSpot.y_ = 1.0f - ((float)offset.y_ + sourceSize.y_ / 2) / rectangle.Height(); } DefineSprite(name, rectangle, hotSpot, offset); } loadXMLFile_.Reset(); loadTextureName_.Clear(); return true; }
void StaticModel::SetModel(Model* model) { if (model == model_) return; // If script erroneously calls StaticModel::SetModel on an AnimatedModel, warn and redirect if (GetType() == AnimatedModel::GetTypeStatic()) { URHO3D_LOGWARNING("StaticModel::SetModel() called on AnimatedModel. Redirecting to AnimatedModel::SetModel()"); AnimatedModel* animatedModel = static_cast<AnimatedModel*>(this); animatedModel->SetModel(model); return; } // Unsubscribe from the reload event of previous model (if any), then subscribe to the new if (model_) UnsubscribeFromEvent(model_, E_RELOADFINISHED); model_ = model; if (model) { SubscribeToEvent(model, E_RELOADFINISHED, URHO3D_HANDLER(StaticModel, HandleModelReloadFinished)); // Copy the subgeometry & LOD level structure SetNumGeometries(model->GetNumGeometries()); const Vector<Vector<SharedPtr<Geometry> > >& geometries = model->GetGeometries(); const PODVector<Vector3>& geometryCenters = model->GetGeometryCenters(); const Matrix3x4* worldTransform = node_ ? &node_->GetWorldTransform() : (const Matrix3x4*)0; for (unsigned i = 0; i < geometries.Size(); ++i) { batches_[i].worldTransform_ = worldTransform; geometries_[i] = geometries[i]; geometryData_[i].center_ = geometryCenters[i]; } SetBoundingBox(model->GetBoundingBox()); ResetLodLevels(); } else { SetNumGeometries(0); SetBoundingBox(BoundingBox()); } MarkNetworkUpdate(); }
bool Texture2DArray::GetData(unsigned layer, unsigned level, void* dest) const { #ifndef GL_ES_VERSION_2_0 if (!object_.name_ || !graphics_) { URHO3D_LOGERROR("Texture array not created, can not get data"); return false; } if (!dest) { URHO3D_LOGERROR("Null destination for getting data"); return false; } if (layer != 0) { URHO3D_LOGERROR("Only the full download of the array is supported, set layer=0"); return false; } if (level >= levels_) { URHO3D_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Getting texture data while device is lost"); return false; } graphics_->SetTextureForUpdate(const_cast<Texture2DArray*>(this)); if (!IsCompressed()) glGetTexImage(target_, level, GetExternalFormat(format_), GetDataType(format_), dest); else glGetCompressedTexImage(target_, level, dest); graphics_->SetTexture(0, 0); return true; #else URHO3D_LOGERROR("Getting texture data not supported"); return false; #endif }
void RenderTargetInfo::Load(const XMLElement& element) { name_ = element.GetAttribute("name"); tag_ = element.GetAttribute("tag"); if (element.HasAttribute("enabled")) enabled_ = element.GetBool("enabled"); if (element.HasAttribute("cubemap")) cubemap_ = element.GetBool("cubemap"); String formatName = element.GetAttribute("format"); format_ = Graphics::GetFormat(formatName); if (element.HasAttribute("filter")) filtered_ = element.GetBool("filter"); if (element.HasAttribute("srgb")) sRGB_ = element.GetBool("srgb"); if (element.HasAttribute("persistent")) persistent_ = element.GetBool("persistent"); if (element.HasAttribute("size")) size_ = element.GetVector2("size"); if (element.HasAttribute("sizedivisor")) { size_ = element.GetVector2("sizedivisor"); sizeMode_ = SIZE_VIEWPORTDIVISOR; } else if (element.HasAttribute("rtsizedivisor")) { // Deprecated rtsizedivisor mode, acts the same as sizedivisor mode now URHO3D_LOGWARNING("Deprecated rtsizedivisor mode used in rendertarget definition"); size_ = element.GetVector2("rtsizedivisor"); sizeMode_ = SIZE_VIEWPORTDIVISOR; } else if (element.HasAttribute("sizemultiplier")) { size_ = element.GetVector2("sizemultiplier"); sizeMode_ = SIZE_VIEWPORTMULTIPLIER; } if (element.HasAttribute("width")) size_.x_ = element.GetFloat("width"); if (element.HasAttribute("height")) size_.y_ = element.GetFloat("height"); }
void Typography::HandleSRGB(StringHash eventType, VariantMap& eventData) { auto* box = static_cast<CheckBox*>(eventData[Toggled::P_ELEMENT].GetPtr()); bool checked = box->IsChecked(); auto* graphics = GetSubsystem<Graphics>(); if (graphics->GetSRGBWriteSupport()) { graphics->SetSRGB(checked); } else { URHO3D_LOGWARNING("Graphics::GetSRGBWriteSupport returned false"); // Note: PostProcess/GammaCorrection.xml implements SRGB conversion. // However, post-processing filters don't affect the UI layer. } }
bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const { if (!object_ || !graphics_) { URHO3D_LOGERROR("No texture created, can not get data"); return false; } if (!dest) { URHO3D_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { URHO3D_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Getting texture data while device is lost"); return false; } if (multiSample_ > 1 && !autoResolve_) { URHO3D_LOGERROR("Can not get data from multisampled texture without autoresolve"); return false; } if (resolveDirty_) graphics_->ResolveToTexture(const_cast<TextureCube*>(this)); graphics_->SetTextureForUpdate(const_cast<TextureCube*>(this)); if (!IsCompressed()) glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, GetExternalFormat(format_), GetDataType(format_), dest); else glGetCompressedTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, dest); graphics_->SetTexture(0, nullptr); return true; }
void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { RayQueryLevel level = query.level_; switch (level) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); break; case RAY_OBB: case RAY_TRIANGLE: { Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); float distance = localRay.HitDistance(boundingBox_); Vector3 normal = -query.ray_.direction_; if (level == RAY_TRIANGLE && distance < query.maxDistance_) { Vector3 geometryNormal; distance = geometry_->GetHitDistance(localRay, &geometryNormal); normal = (node_->GetWorldTransform() * Vector4(geometryNormal, 0.0f)).Normalized(); } if (distance < query.maxDistance_) { RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = normal; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); } } break; case RAY_TRIANGLE_UV: URHO3D_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for TerrainPatch component"); break; } }
VertexDeclaration::VertexDeclaration(Graphics* graphics, const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& elementMasks) : declaration_(0) { unsigned usedElementMask = 0; PODVector<VertexDeclarationElement> elements; for (unsigned i = 0; i < buffers.Size(); ++i) { if (buffers[i]) { unsigned elementMask = elementMasks[i]; if (elementMask == MASK_DEFAULT) elementMask = buffers[i]->GetElementMask(); else { if ((buffers[i]->GetElementMask() & elementMask) != elementMask) return; } if (elementMask & MASK_OBJECTINDEX) URHO3D_LOGWARNING("Object index attribute is not supported on Direct3D9 and will be ignored"); for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j) { VertexElement element = (VertexElement)j; if (elementMask & (1 << j) && !(usedElementMask & (1 << j))) { VertexDeclarationElement newElement; newElement.stream_ = i; newElement.element_ = element; newElement.offset_ = buffers[i]->GetElementOffset(element); usedElementMask |= 1 << j; elements.Push(newElement); } } } } Create(graphics, elements); }
void RigidBody::OnSceneSet(Scene* scene) { if (scene) { if (scene == node_) URHO3D_LOGWARNING(GetTypeName() + " should not be created to the root scene node"); physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>(); physicsWorld_->AddRigidBody(this); AddBodyToWorld(); } else { ReleaseBody(); if (physicsWorld_) physicsWorld_->RemoveRigidBody(this); } }
void Constraint::SetWorldPosition(const Vector3& position) { if (constraint_) { btTransform ownBodyInverse = constraint_->getRigidBodyA().getWorldTransform().inverse(); btTransform otherBodyInverse = constraint_->getRigidBodyB().getWorldTransform().inverse(); btVector3 worldPos = ToBtVector3(position); position_ = (ToVector3(ownBodyInverse * worldPos) + ownBody_->GetCenterOfMass()) / cachedWorldScale_; otherPosition_ = ToVector3(otherBodyInverse * worldPos); if (otherBody_) { otherPosition_ += otherBody_->GetCenterOfMass(); otherPosition_ /= otherBody_->GetNode()->GetWorldScale(); } ApplyFrames(); MarkNetworkUpdate(); } else URHO3D_LOGWARNING("Constraint not created, world position could not be stored"); }
StringHash StringHashRegister::RegisterString(const StringHash& hash, const char* string) { if (mutex_) mutex_->Acquire(); auto iter = map_.find(hash); if (iter == map_.end()) { map_[hash] = string; } else if (iter->compare(string, Qt::CaseInsensitive) != 0) { URHO3D_LOGWARNING(QString::asprintf("StringHash collision detected! Both \"%s\" and \"%s\" have hash #%s", string, qPrintable(*iter), qPrintable(hash.ToString()))); } if (mutex_) mutex_->Release(); return hash; }
void Context::CopyBaseAttributes(StringHash baseType, StringHash derivedType) { // Prevent endless loop if mistakenly copying attributes from same class as derived if (baseType == derivedType) { URHO3D_LOGWARNING("Attempt to copy base attributes to itself for class " + GetTypeName(baseType)); return; } const Vector<AttributeInfo>* baseAttributes = GetAttributes(baseType); if (baseAttributes) { for (unsigned i = 0; i < baseAttributes->Size(); ++i) { const AttributeInfo& attr = baseAttributes->At(i); attributes_[derivedType].Push(attr); if (attr.mode_ & AM_NET) networkAttributes_[derivedType].Push(attr); } } }
bool VertexBuffer::SetData(const void* data) { if (!data) { URHO3D_LOGERROR("Null pointer for vertex buffer data"); return false; } if (!vertexSize_) { URHO3D_LOGERROR("Vertex elements not defined, can not set vertex buffer data"); return false; } if (shadowData_ && data != shadowData_.Get()) memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_); if (object_) { if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Vertex buffer data assignment while device is lost"); dataPending_ = true; return true; } void* hwData = MapBuffer(0, vertexCount_, true); if (hwData) { memcpy(hwData, data, vertexCount_ * vertexSize_); UnmapBuffer(); } else return false; } dataLost_ = false; return true; }
bool Texture3D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture creation while device is lost"); return true; } unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; unsigned d3dUsage = usage_ == TEXTURE_DYNAMIC ? D3DUSAGE_DYNAMIC : 0; IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateVolumeTexture( (UINT)width_, (UINT)height_, (UINT)depth_, requestedLevels_, d3dUsage, (D3DFORMAT)format_, (D3DPOOL)pool, (IDirect3DVolumeTexture9**)&object_, nullptr); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not create texture", hr); URHO3D_SAFE_RELEASE(object_.ptr_); return false; } levels_ = ((IDirect3DVolumeTexture9*)object_.ptr_)->GetLevelCount(); return true; }
bool IndexBuffer::Create() { Release(); if (!indexCount_) return true; if (graphics_) { if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Index buffer creation while device is lost"); return true; } unsigned pool = dynamic_ ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; unsigned d3dUsage = dynamic_ ? D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY : 0; IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateIndexBuffer( indexCount_ * indexSize_, d3dUsage, indexSize_ == sizeof(unsigned) ? D3DFMT_INDEX32 : D3DFMT_INDEX16, (D3DPOOL)pool, (IDirect3DIndexBuffer9**)&object_, nullptr); if (FAILED(hr)) { URHO3D_SAFE_RELEASE(object_.ptr_) URHO3D_LOGD3DERROR("Could not create index buffer", hr); return false; } } return true; }
bool Model::Save(Serializer& dest) const { // Write ID if (!dest.WriteFileID("UMD2")) return false; // Write vertex buffers dest.WriteUInt(vertexBuffers_.Size()); for (unsigned i = 0; i < vertexBuffers_.Size(); ++i) { VertexBuffer* buffer = vertexBuffers_[i]; dest.WriteUInt(buffer->GetVertexCount()); const PODVector<VertexElement>& elements = buffer->GetElements(); dest.WriteUInt(elements.Size()); for (unsigned j = 0; j < elements.Size(); ++j) { unsigned elementDesc = ((unsigned)elements[j].type_) | (((unsigned)elements[j].semantic_) << 8) | (((unsigned)elements[j].index_) << 16); dest.WriteUInt(elementDesc); } dest.WriteUInt(morphRangeStarts_[i]); dest.WriteUInt(morphRangeCounts_[i]); dest.Write(buffer->GetShadowData(), buffer->GetVertexCount() * buffer->GetVertexSize()); } // Write index buffers dest.WriteUInt(indexBuffers_.Size()); for (unsigned i = 0; i < indexBuffers_.Size(); ++i) { IndexBuffer* buffer = indexBuffers_[i]; dest.WriteUInt(buffer->GetIndexCount()); dest.WriteUInt(buffer->GetIndexSize()); dest.Write(buffer->GetShadowData(), buffer->GetIndexCount() * buffer->GetIndexSize()); } // Write geometries dest.WriteUInt(geometries_.Size()); for (unsigned i = 0; i < geometries_.Size(); ++i) { // Write bone mappings dest.WriteUInt(geometryBoneMappings_[i].Size()); for (unsigned j = 0; j < geometryBoneMappings_[i].Size(); ++j) dest.WriteUInt(geometryBoneMappings_[i][j]); // Write the LOD levels dest.WriteUInt(geometries_[i].Size()); for (unsigned j = 0; j < geometries_[i].Size(); ++j) { Geometry* geometry = geometries_[i][j]; dest.WriteFloat(geometry->GetLodDistance()); dest.WriteUInt(geometry->GetPrimitiveType()); dest.WriteUInt(LookupVertexBuffer(geometry->GetVertexBuffer(0), vertexBuffers_)); dest.WriteUInt(LookupIndexBuffer(geometry->GetIndexBuffer(), indexBuffers_)); dest.WriteUInt(geometry->GetIndexStart()); dest.WriteUInt(geometry->GetIndexCount()); } } // Write morphs dest.WriteUInt(morphs_.Size()); for (unsigned i = 0; i < morphs_.Size(); ++i) { dest.WriteString(morphs_[i].name_); dest.WriteUInt(morphs_[i].buffers_.Size()); // Write morph vertex buffers for (HashMap<unsigned, VertexBufferMorph>::ConstIterator j = morphs_[i].buffers_.Begin(); j != morphs_[i].buffers_.End(); ++j) { dest.WriteUInt(j->first_); dest.WriteUInt(j->second_.elementMask_); dest.WriteUInt(j->second_.vertexCount_); // Base size: size of each vertex index unsigned vertexSize = sizeof(unsigned); // Add size of individual elements if (j->second_.elementMask_ & MASK_POSITION) vertexSize += sizeof(Vector3); if (j->second_.elementMask_ & MASK_NORMAL) vertexSize += sizeof(Vector3); if (j->second_.elementMask_ & MASK_TANGENT) vertexSize += sizeof(Vector3); dest.Write(j->second_.morphData_.Get(), vertexSize * j->second_.vertexCount_); } } // Write skeleton skeleton_.Save(dest); // Write bounding box dest.WriteBoundingBox(boundingBox_); // Write geometry centers for (unsigned i = 0; i < geometryCenters_.Size(); ++i) dest.WriteVector3(geometryCenters_[i]); // Write metadata if (HasMetadata()) { File* destFile = dynamic_cast<File*>(&dest); if (destFile) { String xmlName = ReplaceExtension(destFile->GetName(), ".xml"); SharedPtr<XMLFile> xml(new XMLFile(context_)); XMLElement rootElem = xml->CreateRoot("model"); SaveMetadataToXML(rootElem); File xmlFile(context_, xmlName, FILE_WRITE); xml->Save(xmlFile); } else URHO3D_LOGWARNING("Can not save model metadata when not saving into a file"); } return true; }
bool Texture2D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture creation while device is lost"); return true; } unsigned format = GetSRGB() ? GetSRGBFormat(format_) : format_; unsigned externalFormat = GetExternalFormat(format_); unsigned dataType = GetDataType(format_); // Create a renderbuffer instead of a texture if depth texture is not properly supported, or if this will be a packed // depth stencil texture #ifndef GL_ES_VERSION_2_0 if (format == Graphics::GetDepthStencilFormat()) #else if (format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24_OES || format == GL_DEPTH24_STENCIL8_OES || (format == GL_DEPTH_COMPONENT && !graphics_->GetShadowMapFormat())) #endif { if (renderSurface_) { renderSurface_->CreateRenderBuffer(width_, height_, format); return true; } else return false; } glGenTextures(1, &object_); // Ensure that our texture is bound to OpenGL texture unit 0 graphics_->SetTextureForUpdate(this); // If not compressed, create the initial level 0 texture with null data bool success = true; if (!IsCompressed()) { glGetError(); glTexImage2D(target_, 0, format, width_, height_, 0, externalFormat, dataType, 0); if (glGetError()) { URHO3D_LOGERROR("Failed to create texture"); success = false; } } // Set mipmapping levels_ = requestedLevels_; if (!levels_) { unsigned maxSize = Max((int)width_, (int)height_); while (maxSize) { maxSize >>= 1; ++levels_; } }
bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data) { URHO3D_PROFILE(SetTextureData); if (!object_ || !graphics_) { URHO3D_LOGERROR("No texture created, can not set data"); return false; } if (!data) { URHO3D_LOGERROR("Null source for setting data"); return false; } if (level >= levels_) { URHO3D_LOGERROR("Illegal mip level for setting data"); return false; } if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture data assignment while device is lost"); dataPending_ = true; return true; } if (IsCompressed()) { x &= ~3; y &= ~3; } int levelWidth = GetLevelWidth(level); int levelHeight = GetLevelHeight(level); if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0) { URHO3D_LOGERROR("Illegal dimensions for setting data"); return false; } graphics_->SetTextureForUpdate(this); bool wholeLevel = x == 0 && y == 0 && width == levelWidth && height == levelHeight; unsigned format = GetSRGB() ? GetSRGBFormat(format_) : format_; if (!IsCompressed()) { if (wholeLevel) glTexImage2D(target_, level, format, width, height, 0, GetExternalFormat(format_), GetDataType(format_), data); else glTexSubImage2D(target_, level, x, y, width, height, GetExternalFormat(format_), GetDataType(format_), data); } else { if (wholeLevel) glCompressedTexImage2D(target_, level, format, width, height, 0, GetDataSize(width, height), data); else glCompressedTexSubImage2D(target_, level, x, y, width, height, format, GetDataSize(width, height), data); } graphics_->SetTexture(0, 0); return true; }
bool Texture2D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture creation while device is lost"); return true; } #ifdef GL_ES_VERSION_2_0 if (multiSample_ > 1) { URHO3D_LOGWARNING("Multisampled texture is not supported on OpenGL ES"); multiSample_ = 1; autoResolve_ = false; } #endif unsigned format = GetSRGB() ? GetSRGBFormat(format_) : format_; unsigned externalFormat = GetExternalFormat(format_); unsigned dataType = GetDataType(format_); // Create a renderbuffer instead of a texture if depth texture is not properly supported, or if this will be a packed // depth stencil texture #ifndef GL_ES_VERSION_2_0 if (format == Graphics::GetDepthStencilFormat()) #else if (format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24_OES || format == GL_DEPTH24_STENCIL8_OES || (format == GL_DEPTH_COMPONENT && !graphics_->GetShadowMapFormat())) #endif { if (renderSurface_) { renderSurface_->CreateRenderBuffer(width_, height_, format, multiSample_); return true; } else return false; } else { if (multiSample_ > 1) { if (autoResolve_) { // Multisample with autoresolve: create a renderbuffer for rendering, but also a texture renderSurface_->CreateRenderBuffer(width_, height_, format, multiSample_); } else { // Multisample without autoresolve: create a texture only #ifndef GL_ES_VERSION_2_0 if (!Graphics::GetGL3Support() && !GLEW_ARB_texture_multisample) { URHO3D_LOGERROR("Multisampled texture extension not available"); return false; } target_ = GL_TEXTURE_2D_MULTISAMPLE; if (renderSurface_) renderSurface_->target_ = GL_TEXTURE_2D_MULTISAMPLE; #endif } } } glGenTextures(1, &object_.name_); // Ensure that our texture is bound to OpenGL texture unit 0 graphics_->SetTextureForUpdate(this); // If not compressed, create the initial level 0 texture with null data bool success = true; if (!IsCompressed()) { glGetError(); #ifndef GL_ES_VERSION_2_0 if (multiSample_ > 1 && !autoResolve_) glTexImage2DMultisample(target_, multiSample_, format, width_, height_, GL_TRUE); else #endif glTexImage2D(target_, 0, format, width_, height_, 0, externalFormat, dataType, 0); if (glGetError()) { URHO3D_LOGERROR("Failed to create texture"); success = false; } } // Set mipmapping if (usage_ == TEXTURE_DEPTHSTENCIL) requestedLevels_ = 1; else if (usage_ == TEXTURE_RENDERTARGET) { #ifdef __EMSCRIPTEN__ // glGenerateMipmap appears to not be working on WebGL, disable rendertarget mipmaps for now requestedLevels_ = 1; #else if (requestedLevels_ != 1) { // Generate levels for the first time now RegenerateLevels(); // Determine max. levels automatically requestedLevels_ = 0; } #endif } levels_ = CheckMaxLevels(width_, height_, requestedLevels_); #ifndef GL_ES_VERSION_2_0 glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, levels_ - 1); #endif // Set initial parameters, then unbind the texture UpdateParameters(); graphics_->SetTexture(0, 0); return success; }
bool ShaderProgram::Link() { Release(); if (!vertexShader_ || !pixelShader_ || !vertexShader_->GetGPUObject() || !pixelShader_->GetGPUObject()) return false; object_ = glCreateProgram(); if (!object_) { linkerOutput_ = "Could not create shader program"; return false; } // Bind vertex attribute locations to ensure they are the same in all shaders // Note: this is not the same order as in VertexBuffer, instead a remapping is used to ensure everything except cube texture // coordinates fit to the first 8 for better GLES2 device compatibility glBindAttribLocation(object_, 0, "iPos"); glBindAttribLocation(object_, 1, "iNormal"); glBindAttribLocation(object_, 2, "iColor"); glBindAttribLocation(object_, 3, "iTexCoord"); glBindAttribLocation(object_, 4, "iTexCoord2"); glBindAttribLocation(object_, 5, "iTangent"); glBindAttribLocation(object_, 6, "iBlendWeights"); glBindAttribLocation(object_, 7, "iBlendIndices"); glBindAttribLocation(object_, 8, "iCubeTexCoord"); glBindAttribLocation(object_, 9, "iCubeTexCoord2"); #if !defined(GL_ES_VERSION_2_0) || defined(__EMSCRIPTEN__) glBindAttribLocation(object_, 10, "iInstanceMatrix1"); glBindAttribLocation(object_, 11, "iInstanceMatrix2"); glBindAttribLocation(object_, 12, "iInstanceMatrix3"); #endif glAttachShader(object_, vertexShader_->GetGPUObject()); glAttachShader(object_, pixelShader_->GetGPUObject()); glLinkProgram(object_); int linked, length; glGetProgramiv(object_, GL_LINK_STATUS, &linked); if (!linked) { glGetProgramiv(object_, GL_INFO_LOG_LENGTH, &length); linkerOutput_.Resize((unsigned)length); int outLength; glGetProgramInfoLog(object_, length, &outLength, &linkerOutput_[0]); glDeleteProgram(object_); object_ = 0; } else linkerOutput_.Clear(); if (!object_) return false; const int MAX_PARAMETER_NAME_LENGTH = 256; char uniformName[MAX_PARAMETER_NAME_LENGTH]; int uniformCount; glUseProgram(object_); glGetProgramiv(object_, GL_ACTIVE_UNIFORMS, &uniformCount); // Check for constant buffers #ifndef GL_ES_VERSION_2_0 HashMap<unsigned, unsigned> blockToBinding; if (Graphics::GetGL3Support()) { int numUniformBlocks = 0; glGetProgramiv(object_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks); for (int i = 0; i < numUniformBlocks; ++i) { int nameLength; glGetActiveUniformBlockName(object_, (GLuint)i, MAX_PARAMETER_NAME_LENGTH, &nameLength, uniformName); String name(uniformName, (unsigned)nameLength); unsigned blockIndex = glGetUniformBlockIndex(object_, name.CString()); unsigned group = M_MAX_UNSIGNED; // Try to recognize the use of the buffer from its name for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j) { if (name.Contains(shaderParameterGroups[j], false)) { group = j; break; } } // If name is not recognized, search for a digit in the name and use that as the group index if (group == M_MAX_UNSIGNED) { for (unsigned j = 1; j < name.Length(); ++j) { if (name[j] >= '0' && name[j] <= '5') { group = name[j] - '0'; break; } } } if (group >= MAX_SHADER_PARAMETER_GROUPS) { URHO3D_LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() + " " + pixelShader_->GetFullName()); continue; } // Find total constant buffer data size int dataSize; glGetActiveUniformBlockiv(object_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize); if (!dataSize) continue; unsigned bindingIndex = group; // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings // from that point onward if (name.Contains("PS", false)) bindingIndex += MAX_SHADER_PARAMETER_GROUPS; glUniformBlockBinding(object_, blockIndex, bindingIndex); blockToBinding[blockIndex] = bindingIndex; constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(bindingIndex, (unsigned)dataSize); } } #endif // Check for shader parameters and texture units for (int i = 0; i < uniformCount; ++i) { unsigned type; int count; glGetActiveUniform(object_, (GLuint)i, MAX_PARAMETER_NAME_LENGTH, 0, &count, &type, uniformName); int location = glGetUniformLocation(object_, uniformName); // Check for array index included in the name and strip it String name(uniformName); unsigned index = name.Find('['); if (index != String::NPOS) { // If not the first index, skip if (name.Find("[0]", index) == String::NPOS) continue; name = name.Substring(0, index); } if (name[0] == 'c') { // Store constant uniform String paramName = name.Substring(1); ShaderParameter newParam; newParam.type_ = type; newParam.location_ = location; #ifndef GL_ES_VERSION_2_0 // If running OpenGL 3, the uniform may be inside a constant buffer if (newParam.location_ < 0 && Graphics::GetGL3Support()) { int blockIndex, blockOffset; glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex); glGetActiveUniformsiv(object_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset); if (blockIndex >= 0) { newParam.location_ = blockOffset; newParam.bufferPtr_ = constantBuffers_[blockToBinding[blockIndex]]; } } #endif if (newParam.location_ >= 0) shaderParameters_[StringHash(paramName)] = newParam; } else if (location >= 0 && name[0] == 's') { // Set the samplers here so that they do not have to be set later int unit = graphics_->GetTextureUnit(name.Substring(1)); if (unit >= MAX_TEXTURE_UNITS) { // If texture unit name is not recognized, search for a digit in the name and use that as the unit index for (unsigned j = 1; j < name.Length(); ++j) { if (name[j] >= '0' && name[j] <= '9') { unit = name[j] - '0'; break; } } } if (unit < MAX_TEXTURE_UNITS) { useTextureUnit_[unit] = true; glUniform1iv(location, 1, &unit); } } } // Rehash the parameter map to ensure minimal load factor shaderParameters_.Rehash(NextPowerOfTwo(shaderParameters_.Size())); return true; }
bool Texture3D::BeginLoad(Deserializer& source) { auto* cache = GetSubsystem<ResourceCache>(); // In headless mode, do not actually load the texture, just return success if (!graphics_) return true; // If device is lost, retry later if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture load while device is lost"); dataPending_ = true; return true; } String texPath, texName, texExt; SplitPath(GetName(), texPath, texName, texExt); cache->ResetDependencies(this); loadParameters_ = new XMLFile(context_); if (!loadParameters_->Load(source)) { loadParameters_.Reset(); return false; } XMLElement textureElem = loadParameters_->GetRoot(); XMLElement volumeElem = textureElem.GetChild("volume"); XMLElement colorlutElem = textureElem.GetChild("colorlut"); if (volumeElem) { String name = volumeElem.GetAttribute("name"); String volumeTexPath, volumeTexName, volumeTexExt; SplitPath(name, volumeTexPath, volumeTexName, volumeTexExt); // If path is empty, add the XML file path if (volumeTexPath.Empty()) name = texPath + name; loadImage_ = cache->GetTempResource<Image>(name); // Precalculate mip levels if async loading if (loadImage_ && GetAsyncLoadState() == ASYNC_LOADING) loadImage_->PrecalculateLevels(); cache->StoreResourceDependency(this, name); return true; } else if (colorlutElem) { String name = colorlutElem.GetAttribute("name"); String colorlutTexPath, colorlutTexName, colorlutTexExt; SplitPath(name, colorlutTexPath, colorlutTexName, colorlutTexExt); // If path is empty, add the XML file path if (colorlutTexPath.Empty()) name = texPath + name; SharedPtr<File> file = GetSubsystem<ResourceCache>()->GetFile(name); loadImage_ = new Image(context_); if (!loadImage_->LoadColorLUT(*(file.Get()))) { loadParameters_.Reset(); loadImage_.Reset(); return false; } // Precalculate mip levels if async loading if (loadImage_ && GetAsyncLoadState() == ASYNC_LOADING) loadImage_->PrecalculateLevels(); cache->StoreResourceDependency(this, name); return true; } URHO3D_LOGERROR("Texture3D XML data for " + GetName() + " did not contain either volume or colorlut element"); return false; }
bool ShaderProgram::Link() { Release(); if (!vertexShader_ || !pixelShader_ || !vertexShader_->GetGPUObjectName() || !pixelShader_->GetGPUObjectName()) return false; object_.name_ = glCreateProgram(); if (!object_.name_) { linkerOutput_ = "Could not create shader program"; return false; } glAttachShader(object_.name_, vertexShader_->GetGPUObjectName()); glAttachShader(object_.name_, pixelShader_->GetGPUObjectName()); glLinkProgram(object_.name_); int linked, length; glGetProgramiv(object_.name_, GL_LINK_STATUS, &linked); if (!linked) { glGetProgramiv(object_.name_, GL_INFO_LOG_LENGTH, &length); linkerOutput_.Resize((unsigned)length); int outLength; glGetProgramInfoLog(object_.name_, length, &outLength, &linkerOutput_[0]); glDeleteProgram(object_.name_); object_.name_ = 0; } else linkerOutput_.Clear(); if (!object_.name_) return false; const int MAX_NAME_LENGTH = 256; char nameBuffer[MAX_NAME_LENGTH]; int attributeCount, uniformCount, elementCount, nameLength; GLenum type; glUseProgram(object_.name_); // Check for vertex attributes glGetProgramiv(object_.name_, GL_ACTIVE_ATTRIBUTES, &attributeCount); for (int i = 0; i < attributeCount; ++i) { glGetActiveAttrib(object_.name_, i, (GLsizei)MAX_NAME_LENGTH, &nameLength, &elementCount, &type, nameBuffer); String name = String(nameBuffer, nameLength); VertexElementSemantic semantic = MAX_VERTEX_ELEMENT_SEMANTICS; unsigned char semanticIndex = 0; // Go in reverse order so that "binormal" is detected before "normal" for (unsigned j = MAX_VERTEX_ELEMENT_SEMANTICS - 1; j < MAX_VERTEX_ELEMENT_SEMANTICS; --j) { if (name.Contains(ShaderVariation::elementSemanticNames[j], false)) { semantic = (VertexElementSemantic)j; unsigned index = NumberPostfix(name); if (index != M_MAX_UNSIGNED) semanticIndex = (unsigned char)index; break; } } if (semantic == MAX_VERTEX_ELEMENT_SEMANTICS) { URHO3D_LOGWARNING("Found vertex attribute " + name + " with no known semantic in shader program " + vertexShader_->GetFullName() + " " + pixelShader_->GetFullName()); continue; } int location = glGetAttribLocation(object_.name_, name.CString()); vertexAttributes_[MakePair((unsigned char)semantic, semanticIndex)] = location; usedVertexAttributes_ |= (1u << location); } // Check for constant buffers #ifndef GL_ES_VERSION_2_0 HashMap<unsigned, unsigned> blockToBinding; if (Graphics::GetGL3Support()) { int numUniformBlocks = 0; glGetProgramiv(object_.name_, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks); for (int i = 0; i < numUniformBlocks; ++i) { glGetActiveUniformBlockName(object_.name_, (GLuint)i, MAX_NAME_LENGTH, &nameLength, nameBuffer); String name(nameBuffer, (unsigned)nameLength); unsigned blockIndex = glGetUniformBlockIndex(object_.name_, name.CString()); unsigned group = M_MAX_UNSIGNED; // Try to recognize the use of the buffer from its name for (unsigned j = 0; j < MAX_SHADER_PARAMETER_GROUPS; ++j) { if (name.Contains(shaderParameterGroups[j], false)) { group = j; break; } } // If name is not recognized, search for a digit in the name and use that as the group index if (group == M_MAX_UNSIGNED) group = NumberPostfix(name); if (group >= MAX_SHADER_PARAMETER_GROUPS) { URHO3D_LOGWARNING("Skipping unrecognized uniform block " + name + " in shader program " + vertexShader_->GetFullName() + " " + pixelShader_->GetFullName()); continue; } // Find total constant buffer data size int dataSize; glGetActiveUniformBlockiv(object_.name_, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize); if (!dataSize) continue; unsigned bindingIndex = group; // Vertex shader constant buffer bindings occupy slots starting from zero to maximum supported, pixel shader bindings // from that point onward ShaderType shaderType = VS; if (name.Contains("PS", false)) { bindingIndex += MAX_SHADER_PARAMETER_GROUPS; shaderType = PS; } glUniformBlockBinding(object_.name_, blockIndex, bindingIndex); blockToBinding[blockIndex] = bindingIndex; constantBuffers_[bindingIndex] = graphics_->GetOrCreateConstantBuffer(shaderType, bindingIndex, (unsigned)dataSize); } } #endif // Check for shader parameters and texture units glGetProgramiv(object_.name_, GL_ACTIVE_UNIFORMS, &uniformCount); for (int i = 0; i < uniformCount; ++i) { glGetActiveUniform(object_.name_, (GLuint)i, MAX_NAME_LENGTH, nullptr, &elementCount, &type, nameBuffer); int location = glGetUniformLocation(object_.name_, nameBuffer); // Check for array index included in the name and strip it String name(nameBuffer); unsigned index = name.Find('['); if (index != String::NPOS) { // If not the first index, skip if (name.Find("[0]", index) == String::NPOS) continue; name = name.Substring(0, index); } if (name[0] == 'c') { // Store constant uniform String paramName = name.Substring(1); ShaderParameter parameter{paramName, type, location}; bool store = location >= 0; #ifndef GL_ES_VERSION_2_0 // If running OpenGL 3, the uniform may be inside a constant buffer if (parameter.location_ < 0 && Graphics::GetGL3Support()) { int blockIndex, blockOffset; glGetActiveUniformsiv(object_.name_, 1, (const GLuint*)&i, GL_UNIFORM_BLOCK_INDEX, &blockIndex); glGetActiveUniformsiv(object_.name_, 1, (const GLuint*)&i, GL_UNIFORM_OFFSET, &blockOffset); if (blockIndex >= 0) { parameter.offset_ = blockOffset; parameter.bufferPtr_ = constantBuffers_[blockToBinding[blockIndex]]; store = true; } } #endif if (store) shaderParameters_[StringHash(paramName)] = parameter; } else if (location >= 0 && name[0] == 's') { // Set the samplers here so that they do not have to be set later unsigned unit = graphics_->GetTextureUnit(name.Substring(1)); if (unit >= MAX_TEXTURE_UNITS) unit = NumberPostfix(name); if (unit < MAX_TEXTURE_UNITS) { useTextureUnits_[unit] = true; glUniform1iv(location, 1, reinterpret_cast<int*>(&unit)); } } } // Rehash the parameter & vertex attributes maps to ensure minimal load factor vertexAttributes_.Rehash(NextPowerOfTwo(vertexAttributes_.Size())); shaderParameters_.Rehash(NextPowerOfTwo(shaderParameters_.Size())); return true; }
bool TextureCube::BeginLoad(Deserializer& source) { ResourceCache* cache = context_->resourceCache(); // In headless mode, do not actually load the texture, just return success if (!graphics_) return true; // If device is lost, retry later if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture load while device is lost"); dataPending_ = true; return true; } cache->ResetDependencies(this); QString texPath, texName, texExt; SplitPath(GetName(), texPath, texName, texExt); loadParameters_ = (new XMLFile(context_)); if (!loadParameters_->Load(source)) { loadParameters_.Reset(); return false; } loadImages_.clear(); XMLElement textureElem = loadParameters_->GetRoot(); XMLElement imageElem = textureElem.GetChild("image"); // Single image and multiple faces with layout if (imageElem) { QString name = imageElem.GetAttribute("name"); // If path is empty, add the XML file path if (GetPath(name).isEmpty()) name = texPath + name; SharedPtr<Image> image = cache->GetTempResource<Image>(name); if (!image) return false; int faceWidth, faceHeight; loadImages_.resize(MAX_CUBEMAP_FACES); if (image->IsCubemap()) { loadImages_[FACE_POSITIVE_X] = image; loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling(); loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling(); loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling(); loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling(); loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling(); } else { CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout"), cubeMapLayoutNames, CML_HORIZONTAL); switch (layout) { case CML_HORIZONTAL: faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES; faceHeight = image->GetHeight(); loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight); break; case CML_HORIZONTALNVIDIA: faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES; faceHeight = image->GetHeight(); for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i) loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight); break; case CML_HORIZONTALCROSS: faceWidth = image->GetWidth() / 4; faceHeight = image->GetHeight() / 3; loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight); break; case CML_VERTICALCROSS: faceWidth = image->GetWidth() / 3; faceHeight = image->GetHeight() / 4; loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight); if (loadImages_[FACE_NEGATIVE_Z]) { loadImages_[FACE_NEGATIVE_Z]->FlipVertical(); loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal(); } break; case CML_BLENDER: faceWidth = image->GetWidth() / 3; faceHeight = image->GetHeight() / 2; loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight); loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight); loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight); break; } } } // Face per image else { XMLElement faceElem = textureElem.GetChild("face"); while (faceElem) { QString name = faceElem.GetAttribute("name"); // If path is empty, add the XML file path if (GetPath(name).isEmpty()) name = texPath + name; loadImages_.push_back(cache->GetTempResource<Image>(name)); cache->StoreResourceDependency(this, name); faceElem = faceElem.GetNext("face"); } } // Precalculate mip levels if async loading if (GetAsyncLoadState() == ASYNC_LOADING) { for (unsigned i = 0; i < loadImages_.size(); ++i) { if (loadImages_[i]) loadImages_[i]->PrecalculateLevels(); } } return true; }
void SceneResolver::Resolve() { // Nodes do not have component or node ID attributes, so only have to go through components ea::hash_set<StringHash> noIDAttributes; for (auto i = components_.begin(); i != components_.end(); ++i) { Component* component = i->second; if (!component || noIDAttributes.contains(component->GetType())) continue; bool hasIDAttributes = false; const ea::vector<AttributeInfo>* attributes = component->GetAttributes(); if (!attributes) { noIDAttributes.insert(component->GetType()); continue; } for (unsigned j = 0; j < attributes->size(); ++j) { const AttributeInfo& info = attributes->at(j); if (info.mode_ & AM_NODEID) { hasIDAttributes = true; unsigned oldNodeID = component->GetAttribute(j).GetUInt(); if (oldNodeID) { auto k = nodes_.find(oldNodeID); if (k != nodes_.end() && k->second) { unsigned newNodeID = k->second->GetID(); component->SetAttribute(j, Variant(newNodeID)); } else URHO3D_LOGWARNING("Could not resolve node ID " + ea::to_string(oldNodeID)); } } else if (info.mode_ & AM_COMPONENTID) { hasIDAttributes = true; unsigned oldComponentID = component->GetAttribute(j).GetUInt(); if (oldComponentID) { auto k = components_.find( oldComponentID); if (k != components_.end() && k->second) { unsigned newComponentID = k->second->GetID(); component->SetAttribute(j, Variant(newComponentID)); } else URHO3D_LOGWARNING("Could not resolve component ID " + ea::to_string(oldComponentID)); } } else if (info.mode_ & AM_NODEIDVECTOR) { hasIDAttributes = true; Variant attrValue = component->GetAttribute(j); const VariantVector& oldNodeIDs = attrValue.GetVariantVector(); if (oldNodeIDs.size()) { // The first index stores the number of IDs redundantly. This is for editing unsigned numIDs = oldNodeIDs[0].GetUInt(); VariantVector newIDs; newIDs.push_back(numIDs); for (unsigned k = 1; k < oldNodeIDs.size(); ++k) { unsigned oldNodeID = oldNodeIDs[k].GetUInt(); auto l = nodes_.find(oldNodeID); if (l != nodes_.end() && l->second) newIDs.push_back(l->second->GetID()); else { // If node was not found, retain number of elements, just store ID 0 newIDs.push_back(0); URHO3D_LOGWARNING("Could not resolve node ID " + ea::to_string(oldNodeID)); } } component->SetAttribute(j, newIDs); } } } // If component type had no ID attributes, cache this fact for optimization if (!hasIDAttributes) noIDAttributes.insert(component->GetType()); } // Attributes have been resolved, so no need to remember the nodes after this Reset(); }
bool Texture2D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Texture creation while device is lost"); return true; } if (multiSample_ > 1 && !autoResolve_) { URHO3D_LOGWARNING("Multisampled texture without autoresolve is not supported on Direct3D9"); autoResolve_ = true; } GraphicsImpl* impl = graphics_->GetImpl(); unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; unsigned d3dUsage = 0; switch (usage_) { case TEXTURE_DYNAMIC: d3dUsage |= D3DUSAGE_DYNAMIC; break; case TEXTURE_RENDERTARGET: d3dUsage |= D3DUSAGE_RENDERTARGET; if (requestedLevels_ != 1) { // Check mipmap autogeneration support if (impl->CheckFormatSupport((D3DFORMAT)format_, D3DUSAGE_AUTOGENMIPMAP, D3DRTYPE_TEXTURE)) { requestedLevels_ = 0; d3dUsage |= D3DUSAGE_AUTOGENMIPMAP; } else requestedLevels_ = 1; } break; case TEXTURE_DEPTHSTENCIL: d3dUsage |= D3DUSAGE_DEPTHSTENCIL; // No mipmaps for depth-stencil textures requestedLevels_ = 1; break; default: break; } if (multiSample_ > 1) { // Fall back to non-multisampled if unsupported multisampling mode if (!impl->CheckMultiSampleSupport((D3DFORMAT)format_, multiSample_)) { multiSample_ = 1; autoResolve_ = false; } } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); // If creating a depth-stencil texture, and it is not supported, create a depth-stencil surface instead // Multisampled surfaces need also to be created this way if (usage_ == TEXTURE_DEPTHSTENCIL && (multiSample_ > 1 || !graphics_->GetImpl()->CheckFormatSupport((D3DFORMAT)format_, d3dUsage, D3DRTYPE_TEXTURE))) { HRESULT hr = device->CreateDepthStencilSurface( (UINT)width_, (UINT)height_, (D3DFORMAT)format_, (multiSample_ > 1) ? (D3DMULTISAMPLE_TYPE)multiSample_ : D3DMULTISAMPLE_NONE, 0, FALSE, (IDirect3DSurface9**)&renderSurface_->surface_, nullptr); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not create depth-stencil surface", hr); URHO3D_SAFE_RELEASE(renderSurface_->surface_); return false; } levels_ = 1; } else { HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture( (UINT)width_, (UINT)height_, requestedLevels_, d3dUsage, (D3DFORMAT)format_, (D3DPOOL)pool, (IDirect3DTexture9**)&object_, nullptr); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not create texture", hr); URHO3D_SAFE_RELEASE(object_.ptr_); return false; } levels_ = ((IDirect3DTexture9*)object_.ptr_)->GetLevelCount(); // Create the multisampled rendertarget for rendering to if necessary if (usage_ == TEXTURE_RENDERTARGET && multiSample_ > 1) { HRESULT hr = device->CreateRenderTarget( (UINT)width_, (UINT)height_, (D3DFORMAT)format_, (D3DMULTISAMPLE_TYPE)multiSample_, 0, FALSE, (IDirect3DSurface9**)&renderSurface_->surface_, nullptr); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not create multisampled rendertarget surface", hr); URHO3D_SAFE_RELEASE(renderSurface_->surface_); return false; } } else if (usage_ >= TEXTURE_RENDERTARGET) { // Else use the texture surface directly for rendering hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&renderSurface_->surface_); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not get rendertarget surface", hr); URHO3D_SAFE_RELEASE(renderSurface_->surface_); return false; } } } return true; }
bool Texture2D::GetData(unsigned level, void* dest) const { if (!object_.ptr_) { URHO3D_LOGERROR("No texture created, can not get data"); return false; } if (!dest) { URHO3D_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { URHO3D_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { URHO3D_LOGWARNING("Getting texture data while device is lost"); return false; } if (resolveDirty_) graphics_->ResolveToTexture(const_cast<Texture2D*>(this)); int levelWidth = GetLevelWidth(level); int levelHeight = GetLevelHeight(level); D3DLOCKED_RECT d3dLockedRect; RECT d3dRect; d3dRect.left = 0; d3dRect.top = 0; d3dRect.right = levelWidth; d3dRect.bottom = levelHeight; IDirect3DSurface9* offscreenSurface = nullptr; // Need to use a offscreen surface & GetRenderTargetData() for rendertargets if (renderSurface_) { if (level != 0) { URHO3D_LOGERROR("Can only get mip level 0 data from a rendertarget"); return false; } // If multisampled, must copy the surface of the resolve texture instead of the multisampled surface IDirect3DSurface9* resolveSurface = nullptr; if (multiSample_ > 1) { HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&resolveSurface); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not get surface of the resolve texture", hr); URHO3D_SAFE_RELEASE(resolveSurface); return false; } } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, nullptr); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not create surface for getting rendertarget data", hr); URHO3D_SAFE_RELEASE(offscreenSurface) URHO3D_SAFE_RELEASE(resolveSurface); return false; } if (resolveSurface) hr = device->GetRenderTargetData(resolveSurface, offscreenSurface); else hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface); URHO3D_SAFE_RELEASE(resolveSurface); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not get rendertarget data", hr); URHO3D_SAFE_RELEASE(offscreenSurface); return false; } hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr); URHO3D_SAFE_RELEASE(offscreenSurface); return false; } } else { HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { URHO3D_LOGD3DERROR("Could not lock texture", hr); return false; } } int height = levelHeight; if (IsCompressed()) height = (height + 3) >> 2; unsigned char* destPtr = (unsigned char*)dest; unsigned rowSize = GetRowDataSize(levelWidth); // GetRowDataSize() returns CPU-side (destination) data size, so need to convert for X8R8G8B8 if (format_ == D3DFMT_X8R8G8B8) rowSize = rowSize / 3 * 4; // Perform conversion to RGB / RGBA as necessary switch (format_) { default: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; memcpy(destPtr, src, rowSize); destPtr += rowSize; } break; case D3DFMT_X8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; ++src; destPtr += 3; } } break; case D3DFMT_A8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; destPtr[3] = *src++; destPtr += 4; } } break; } if (offscreenSurface) offscreenSurface->UnlockRect(); else ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level); URHO3D_SAFE_RELEASE(offscreenSurface); return true; }