float Geometry::GetHitDistance(const Ray& ray, Vector3* outNormal, Vector2* outUV) const { const unsigned char* vertexData; const unsigned char* indexData; unsigned vertexSize; unsigned indexSize; const PODVector<VertexElement>* elements; GetRawData(vertexData, vertexSize, indexData, indexSize, elements); if (!vertexData || !elements || VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR3, SEM_POSITION) != 0) return M_INFINITY; unsigned uvOffset = VertexBuffer::GetElementOffset(*elements, TYPE_VECTOR2, SEM_TEXCOORD); if (outUV && uvOffset == M_MAX_UNSIGNED) { // requested UV output, but no texture data in vertex buffer ATOMIC_LOGWARNING("Illegal GetHitDistance call: UV return requested on vertex buffer without UV coords"); *outUV = Vector2::ZERO; outUV = 0; } return indexData ? ray.HitDistance(vertexData, vertexSize, indexData, indexSize, indexStart_, indexCount_, outNormal, outUV, uvOffset) : ray.HitDistance(vertexData, vertexSize, vertexStart_, vertexCount_, outNormal, outUV, uvOffset); }
bool UnknownComponent::SaveXML(XMLElement& dest) const { if (dest.IsNull()) { ATOMIC_LOGERROR("Could not save " + GetTypeName() + ", null destination element"); return false; } if (!useXML_) ATOMIC_LOGWARNING("UnknownComponent loaded in binary or JSON mode, attributes will be empty for XML save"); // Write type and ID if (!dest.SetString("type", GetTypeName())) return false; if (!dest.SetInt("id", id_)) return false; for (unsigned i = 0; i < xmlAttributeInfos_.Size(); ++i) { XMLElement attrElem = dest.CreateChild("attribute"); attrElem.SetAttribute("name", xmlAttributeInfos_[i].name_); attrElem.SetAttribute("value", xmlAttributes_[i]); } return true; }
bool Texture2D::GetData(unsigned level, void* dest) const { if (!object_.name_ || !graphics_) { ATOMIC_LOGERROR("No texture created, can not get data"); return false; } #ifndef GL_ES_VERSION_2_0 if (!dest) { ATOMIC_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { ATOMIC_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Getting texture data while device is lost"); return false; } if (multiSample_ > 1 && !autoResolve_) { ATOMIC_LOGERROR("Can not get data from multisampled texture without autoresolve"); return false; } if (resolveDirty_) graphics_->ResolveToTexture(const_cast<Texture2D*>(this)); graphics_->SetTextureForUpdate(const_cast<Texture2D*>(this)); if (!IsCompressed()) glGetTexImage(target_, level, GetExternalFormat(format_), GetDataType(format_), dest); else glGetCompressedTexImage(target_, level, dest); graphics_->SetTexture(0, 0); return true; #else // Special case on GLES: if the texture is a rendertarget, can make it current and use glReadPixels() if (usage_ == TEXTURE_RENDERTARGET) { graphics_->SetRenderTarget(0, const_cast<Texture2D*>(this)); // Ensure the FBO is current; this viewport is actually never rendered to graphics_->SetViewport(IntRect(0, 0, width_, height_)); glReadPixels(0, 0, width_, height_, GetExternalFormat(format_), GetDataType(format_), dest); return true; } ATOMIC_LOGERROR("Getting texture data not supported"); return false; #endif }
bool Animation::Save(Serializer& dest) const { // Write ID, name and length dest.WriteFileID("UANI"); dest.WriteString(animationName_); dest.WriteFloat(length_); // Write tracks dest.WriteUInt(tracks_.Size()); for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks_.Begin(); i != tracks_.End(); ++i) { const AnimationTrack& track = i->second_; dest.WriteString(track.name_); dest.WriteUByte(track.channelMask_); dest.WriteUInt(track.keyFrames_.Size()); // Write keyframes of the track for (unsigned j = 0; j < track.keyFrames_.Size(); ++j) { const AnimationKeyFrame& keyFrame = track.keyFrames_[j]; dest.WriteFloat(keyFrame.time_); if (track.channelMask_ & CHANNEL_POSITION) dest.WriteVector3(keyFrame.position_); if (track.channelMask_ & CHANNEL_ROTATION) dest.WriteQuaternion(keyFrame.rotation_); if (track.channelMask_ & CHANNEL_SCALE) dest.WriteVector3(keyFrame.scale_); } } // If triggers have been defined, write an XML file for them if (!triggers_.Empty() || 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("animation"); for (unsigned i = 0; i < triggers_.Size(); ++i) { XMLElement triggerElem = rootElem.CreateChild("trigger"); triggerElem.SetFloat("time", triggers_[i].time_); triggerElem.SetVariant(triggers_[i].data_); } SaveMetadataToXML(rootElem); File xmlFile(context_, xmlName, FILE_WRITE); xml->Save(xmlFile); } else ATOMIC_LOGWARNING("Can not save animation trigger data when not saving into a file"); } return true; }
void CustomGeometry::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_) { distance = M_INFINITY; for (unsigned i = 0; i < batches_.Size(); ++i) { Geometry* geometry = batches_[i].geometry_; if (geometry) { Vector3 geometryNormal; float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal); if (geometryDistance < query.maxDistance_ && geometryDistance < distance) { distance = geometryDistance; 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: ATOMIC_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for CustomGeometry component"); break; } }
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()) { ATOMIC_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, ATOMIC_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]; // ATOMIC BEGIN geometryData_[i].enabled_ = true; geometryData_[i].batchGeometry_ = 0; // ATOMIC END } SetBoundingBox(model->GetBoundingBox()); ResetLodLevels(); } else { SetNumGeometries(0); SetBoundingBox(BoundingBox()); } MarkNetworkUpdate(); }
void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // Do not record a raycast result for a directional light, as it would block all other results if (lightType_ == LIGHT_DIRECTIONAL) return; float distance = query.maxDistance_; switch (query.level_) { case RAY_AABB: Drawable::ProcessRayQuery(query, results); return; case RAY_OBB: { Matrix3x4 inverse(node_->GetWorldTransform().Inverse()); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(GetWorldBoundingBox().Transformed(inverse)); if (distance >= query.maxDistance_) return; } break; case RAY_TRIANGLE: if (lightType_ == LIGHT_SPOT) { distance = query.ray_.HitDistance(GetFrustum()); if (distance >= query.maxDistance_) return; } else { distance = query.ray_.HitDistance(Sphere(node_->GetWorldPosition(), range_)); if (distance >= query.maxDistance_) return; } break; case RAY_TRIANGLE_UV: ATOMIC_LOGWARNING("RAY_TRIANGLE_UV query level is not supported for Light component"); return; } // If the code reaches here then we have a hit RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = M_MAX_UNSIGNED; results.Push(result); }
bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard) { if (start == 0 && count == vertexCount_) return SetData(data); if (!data) { ATOMIC_LOGERROR("Null pointer for vertex buffer data"); return false; } if (!vertexSize_) { ATOMIC_LOGERROR("Vertex elements not defined, can not set vertex buffer data"); return false; } if (start + count > vertexCount_) { ATOMIC_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_.ptr_) { if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Vertex buffer data assignment while device is lost"); dataPending_ = true; return true; } void* hwData = MapBuffer(start, count, discard); if (hwData) { memcpy(hwData, data, count * vertexSize_); UnmapBuffer(); } else return false; } return true; }
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) { ATOMIC_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 UnknownComponent::Save(Serializer& dest) const { if (useXML_) ATOMIC_LOGWARNING("UnknownComponent loaded in XML mode, attributes will be empty for binary save"); // Write type and ID if (!dest.WriteStringHash(GetType())) return false; if (!dest.WriteUInt(id_)) return false; if (!binaryAttributes_.Size()) return true; else return dest.Write(&binaryAttributes_[0], binaryAttributes_.Size()) == binaryAttributes_.Size(); }
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 ATOMIC_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 Context::CopyBaseAttributes(StringHash baseType, StringHash derivedType) { // Prevent endless loop if mistakenly copying attributes from same class as derived if (baseType == derivedType) { ATOMIC_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) { ATOMIC_LOGERROR("Null pointer for vertex buffer data"); return false; } if (!vertexSize_) { ATOMIC_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_.ptr_) { if (graphics_->IsDeviceLost()) { ATOMIC_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 UnknownComponent::SaveJSON(JSONValue& dest) const { if (!useXML_) ATOMIC_LOGWARNING("UnknownComponent loaded in binary mode, attributes will be empty for JSON save"); // Write type and ID dest.Set("type", GetTypeName()); dest.Set("id", (int) id_); JSONArray attributesArray; attributesArray.Reserve(xmlAttributeInfos_.Size()); for (unsigned i = 0; i < xmlAttributeInfos_.Size(); ++i) { JSONValue attrVal; attrVal.Set("name", xmlAttributeInfos_[i].name_); attrVal.Set("value", xmlAttributes_[i]); attributesArray.Push(attrVal); } dest.Set("attributes", attributesArray); return true; }
bool VertexBuffer::Create() { Release(); if (!vertexCount_ || elements_.Empty()) return true; if (graphics_) { if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Vertex 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->CreateVertexBuffer( vertexCount_ * vertexSize_, d3dUsage, 0, (D3DPOOL)pool, (IDirect3DVertexBuffer9**)&object_.ptr_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); ATOMIC_LOGD3DERROR("Could not create vertex buffer", hr); return false; } } return true; }
bool IndexBuffer::Create() { Release(); if (!indexCount_) return true; if (graphics_) { if (graphics_->IsDeviceLost()) { ATOMIC_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_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_) ATOMIC_LOGD3DERROR("Could not create index buffer", hr); return false; } } return true; }
bool Texture2D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Texture creation while device is lost"); return true; } #ifdef GL_ES_VERSION_2_0 if (multiSample_ > 1) { ATOMIC_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) { ATOMIC_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()) { ATOMIC_LOGERROR("Failed to create texture"); success = false; } } // Set mipmapping if (usage_ == TEXTURE_DEPTHSTENCIL) requestedLevels_ = 1; else if (usage_ == TEXTURE_RENDERTARGET) { #if defined(__EMSCRIPTEN__) || defined(IOS) // glGenerateMipmap appears to not be working on WebGL or iOS, 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 ShaderVariation::Compile() { const String& sourceCode = owner_->GetSourceCode(type_); Vector<String> defines = defines_.Split(' '); // Set the entrypoint, profile and flags according to the shader being compiled const char* entryPoint = 0; const char* profile = 0; unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; if (type_ == VS) { entryPoint = "VS"; defines.Push("COMPILEVS"); profile = "vs_3_0"; } else { entryPoint = "PS"; defines.Push("COMPILEPS"); profile = "ps_3_0"; flags |= D3DCOMPILE_PREFER_FLOW_CONTROL; } defines.Push("MAXBONES=" + String(Graphics::GetMaxBones())); // Collect defines into macros Vector<String> defineValues; PODVector<D3D_SHADER_MACRO> macros; for (unsigned i = 0; i < defines.Size(); ++i) { unsigned equalsPos = defines[i].Find('='); if (equalsPos != String::NPOS) { defineValues.Push(defines[i].Substring(equalsPos + 1)); defines[i].Resize(equalsPos); } else defineValues.Push("1"); } for (unsigned i = 0; i < defines.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = defines[i].CString(); macro.Definition = defineValues[i].CString(); macros.Push(macro); // In debug mode, check that all defines are referenced by the shader code #ifdef _DEBUG if (sourceCode.Find(defines[i]) == String::NPOS) ATOMIC_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]); #endif } D3D_SHADER_MACRO endMacro; endMacro.Name = 0; endMacro.Definition = 0; macros.Push(endMacro); // Compile using D3DCompile ID3DBlob* shaderCode = 0; ID3DBlob* errorMsgs = 0; HRESULT hr = D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), ¯os.Front(), 0, entryPoint, profile, flags, 0, &shaderCode, &errorMsgs); if (FAILED(hr)) { // Do not include end zero unnecessarily compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), (unsigned)errorMsgs->GetBufferSize() - 1); } else { if (type_ == VS) ATOMIC_LOGDEBUG("Compiled vertex shader " + GetFullName()); else ATOMIC_LOGDEBUG("Compiled pixel shader " + GetFullName()); // Inspect the produced bytecode using MojoShader, then strip and store it unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer(); unsigned bufSize = (unsigned)shaderCode->GetBufferSize(); ParseParameters(bufData, bufSize); CopyStrippedCode(byteCode_, bufData, bufSize); } ATOMIC_SAFE_RELEASE(shaderCode); ATOMIC_SAFE_RELEASE(errorMsgs); return !byteCode_.Empty(); }
bool Texture2D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Texture creation while device is lost"); return true; } 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; break; case TEXTURE_DEPTHSTENCIL: d3dUsage |= D3DUSAGE_DEPTHSTENCIL; break; default: break; } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); // If creating a depth-stencil texture, and it is not supported, create a depth-stencil surface instead if (usage_ == TEXTURE_DEPTHSTENCIL && !graphics_->GetImpl()->CheckFormatSupport((D3DFORMAT)format_, d3dUsage, D3DRTYPE_TEXTURE)) { HRESULT hr = device->CreateDepthStencilSurface( (UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DMULTISAMPLE_NONE, 0, FALSE, (IDirect3DSurface9**)&renderSurface_->surface_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(renderSurface_->surface_); ATOMIC_LOGD3DERROR("Could not create depth-stencil surface", hr); return false; } levels_ = 1; } else { HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture( (UINT)width_, (UINT)height_, requestedLevels_, d3dUsage, (D3DFORMAT)format_, (D3DPOOL)pool, (IDirect3DTexture9**)&object_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); ATOMIC_LOGD3DERROR("Could not create texture", hr); return false; } levels_ = ((IDirect3DTexture9*)object_.ptr_)->GetLevelCount(); if (usage_ >= TEXTURE_RENDERTARGET) { hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&renderSurface_->surface_); if (FAILED(hr)) ATOMIC_LOGD3DERROR("Could not get rendertarget surface", hr); } } return true; }
bool Texture2D::GetData(unsigned level, void* dest) const { if (!object_.ptr_) { ATOMIC_LOGERROR("No texture created, can not get data"); return false; } if (!dest) { ATOMIC_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { ATOMIC_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Getting texture data while device is lost"); return false; } 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 = 0; // Need to use a offscreen surface & GetRenderTargetData() for rendertargets if (renderSurface_) { if (level != 0) { ATOMIC_LOGERROR("Can only get mip level 0 data from a rendertarget"); return false; } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(offscreenSurface); ATOMIC_LOGD3DERROR("Could not create surface for getting rendertarget data", hr); return false; } hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not get rendertarget data", hr); offscreenSurface->Release(); return false; } hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr); offscreenSurface->Release(); return false; } } else { HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { ATOMIC_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(); offscreenSurface->Release(); } else ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level); return true; }
bool Texture3D::BeginLoad(Deserializer& source) { ResourceCache* 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()) { ATOMIC_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; } ATOMIC_LOGERROR("Texture3D XML data for " + GetName() + " did not contain either volume or colorlut element"); return false; }
bool TextureCube::BeginLoad(Deserializer& source) { ResourceCache* 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()) { ATOMIC_LOGWARNING("Texture load while device is lost"); dataPending_ = true; return true; } cache->ResetDependencies(this); String 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) { String name = imageElem.GetAttribute("name"); // If path is empty, add the XML file path if (GetPath(name).Empty()) 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").CString(), 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) { String name = faceElem.GetAttribute("name"); // If path is empty, add the XML file path if (GetPath(name).Empty()) name = texPath + name; loadImages_.Push(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; }
bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data) { ATOMIC_PROFILE(SetTextureData); if (!object_.name_ || !graphics_) { ATOMIC_LOGERROR("No texture created, can not set data"); return false; } if (!data) { ATOMIC_LOGERROR("Null source for setting data"); return false; } if (level >= levels_) { ATOMIC_LOGERROR("Illegal mip level for setting data"); return false; } if (graphics_->IsDeviceLost()) { ATOMIC_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) { ATOMIC_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; }