TextureDesc LoadTextureFormat(StringSection<::Assets::ResChar> filename) { ucs2 wfilename[MaxPath]; Conversion::Convert(wfilename, dimof(wfilename), filename.begin(), filename.end()); auto fmt = GetTexFmt(wfilename); using namespace DirectX; TexMetadata metadata; HRESULT hresult = -1; if (fmt == TexFmt::DDS) { hresult = GetMetadataFromDDSFile((const wchar_t*)wfilename, DDS_FLAGS_NONE, metadata); } else if (fmt == TexFmt::TGA) { hresult = GetMetadataFromTGAFile((const wchar_t*)wfilename, metadata); } else if (fmt == TexFmt::WIC) { hresult = GetMetadataFromWICFile((const wchar_t*)wfilename, WIC_FLAGS_NONE, metadata); } else { LogWarning << "Texture format not apparent from filename (" << filename.AsString().c_str() << ")"; } if (SUCCEEDED(hresult)) { return BuildTextureDesc(metadata); } return TextureDesc::Empty(); }
void Execute(StringSection<char> cmdLine) { // We're going to run a simple process that loads a texture file, runs some shader // process, and then writes out an output file. // This is a command line app; so our instructions should be on the command line. // We're going to use a stream formatter & our "Document" asbtraction to interpret // the command line. // We could replace the formatter with a version specialized for // command lines if we wanted a unix style command line syntax (and, actually, some // syntax elements of this formatter [like ';'] might conflict on some OSs. MemoryMappedInputStream stream(cmdLine.begin(), cmdLine.end()); InputStreamFormatter<char> formatter(stream); Document<InputStreamFormatter<char>> doc(formatter); auto outputFile = doc.Attribute("o").Value(); auto shader = doc.Attribute("s").Value(); if (outputFile.Empty() || shader.Empty()) { return; } auto xleDir = GetEnv("XLE_DIR"); if (xleDir.empty()) { LogAlwaysError << "XLE_DIR environment variable isn't set. Expecting this to be set to root XLE directory"; LogAlwaysError << "This program loads shaders from the $(XLE_DIR)\\Working\\Game\\xleres folder"; return; } // we can now construct basic services auto cleanup = MakeAutoCleanup([]() { TerminateFileSystemMonitoring(); }); auto device = RenderCore::CreateDevice(); Samples::MinimalAssetServices services(device.get()); // We need to think about SRGB modes... do we want to do the processing in // linear or SRGB space? So we want to write out a linear or SRB texture? auto shaderParameters = CreateParameterBox(doc.Element("p")); auto resultTexture = ExecuteTransform( *device, MakeStringSection(xleDir), shader, shaderParameters, { { "Sky", HosekWilkieSky }, { "Compress", CompressTexture } }); if (!resultTexture._pkt) { LogAlwaysError << "Error while performing texture transform"; return; } // save "readback" as an output texture. // We will write a uncompressed format; normally a second command line // tool will be used to compress the result. resultTexture.Save(outputFile.AsString().c_str()); }
std::shared_ptr<DependencyValidation> Store::WriteDependencies( const ResChar intermediateFileName[], StringSection<ResChar> baseDir, IteratorRange<const DependentFileState*> deps, bool makeDepValidation) const { Data data; std::shared_ptr<DependencyValidation> result; if (makeDepValidation) result = std::make_shared<DependencyValidation>(); // we have to write the base directory to the dependencies file as well // to keep it short, most filenames should be expressed as relative files char buffer[MaxPath]; data.SetAttribute("BasePath", baseDir.AsString().c_str()); SplitPath<ResChar> baseSplitPath(baseDir); auto dependenciesBlock = std::make_unique<Data>("Dependencies"); for (auto& s:deps) { auto c = std::make_unique<Data>(); auto relPath = MakeRelativePath( baseSplitPath, SplitPath<ResChar>(s._filename)); c->SetValue(relPath.c_str()); if (s._status != DependentFileState::Status::Shadowed) { c->SetAttribute("ModTimeH", (int)(s._timeMarker>>32ull)); c->SetAttribute("ModTimeL", (int)(s._timeMarker)); } dependenciesBlock->Add(c.release()); if (makeDepValidation) RegisterFileDependency(result, s._filename.c_str()); }
void PushString( std::basic_streambuf<OutChar>& stream, StringSection<InChar> input) { // String conversion process results in several redundant allocations. It's not perfectly // efficient using OutputString = std::basic_string<OutChar>; auto converted = Conversion::Convert<OutputString>(input.AsString()); stream.sputn(AsPointer(converted.begin()), converted.size()); }
void DirectorySearchRules::AddSearchDirectory(StringSection<ResChar> dir) { // Attempt to fit this directory into our buffer. // note that we have limited space in the buffer, but we can't really // rely on all directory names remaining small and convenient... If we // overflow our fixed size buffer, we can use the dynamically // allocated "_bufferOverflow" assert((_startPointCount+1) <= dimof(_startOffsets)); if ((_startPointCount+1) > dimof(_startOffsets)) { // limited number of directories that can be pushed into a single "search rules" // this allows us to avoid a little bit of awkward dynamic memory allocation return; } // Check for duplicates // Duplicates are bad because they will increase the number of search operations if (HasDirectory(dir)) return; unsigned allocationLength = (unsigned)(dir.Length() + 1); if (_bufferOverflow.empty() && (_bufferUsed + allocationLength <= dimof(_buffer))) { // just append this new string to our buffer, and add a new start offset XlCopyMemory(&_buffer[_bufferUsed], dir.begin(), (allocationLength-1) * sizeof(ResChar)); _buffer[_bufferUsed+allocationLength-1] = '\0'; } else { if (_bufferOverflow.empty()) { _bufferOverflow.resize(_bufferUsed + allocationLength); XlCopyMemory(AsPointer(_bufferOverflow.begin()), _buffer, _bufferUsed * sizeof(ResChar)); XlCopyMemory(PtrAdd(AsPointer(_bufferOverflow.begin()), _bufferUsed * sizeof(ResChar)), dir.begin(), (allocationLength-1) * sizeof(ResChar)); _bufferOverflow[_bufferUsed+allocationLength-1] = '\0'; } else { assert(_bufferOverflow.size() == allocationLength); auto i = _bufferOverflow.insert(_bufferOverflow.end(), dir.begin(), dir.end()); _bufferOverflow.insert(i + dir.Length(), 0); } } _startOffsets[_startPointCount++] = _bufferUsed; _bufferUsed += allocationLength; }
intrusive_ptr<DataPacket> CreateStreamingTextureSource( StringSection<::Assets::ResChar> filename, TextureLoadFlags::BitField flags) { return make_intrusive<StreamingTexture>(filename.begin(), filename.end(), flags); }
PreparedSkinFile::PreparedSkinFile(const ColladaScaffold& input, const VisualScene& scene, StringSection<utf8> rootNode) { using namespace RenderCore::ColladaConversion; SkeletonRegistry jointRefs; ReferencedGeometries refGeos; bool gatherSuccess = refGeos.Gather(scene.GetRootNode(), rootNode, jointRefs); if (!gatherSuccess) { StringMeld<1024> meld; using ::operator<<; meld << "Error while looking for root node: " << rootNode.AsString().c_str() << ". Known nodes: "; auto nodes = scene.GetRootNode().FindAllBreadthFirst([](const Node& n) { return true;}); for (unsigned c=0; c<nodes.size(); ++c) { if (c!=0) meld << ", "; meld << nodes[c].GetName().AsString().c_str(); } Throw(::Assets::Exceptions::FormatError(meld.get())); } // The skeleton joints won't be included in the skeleton // until we call FindSkinJoints. We don't really need the // full skeleton embedded in the skin file... When the skeleton // is not there, we will just see the model in the bind pose. // But it can be handy to be there for previewing models quickly. refGeos.FindSkinJoints(scene, input._resolveContext, jointRefs); // We can now build the skeleton (because ReferencedGeometries::Gather // has initialised jointRefs. unsigned topLevelPops = 0; auto coordinateTransform = BuildCoordinateTransform(input._doc->GetAssetDesc()); if (!Equivalent(coordinateTransform, Identity<Float4x4>(), 1e-5f)) { // Push on the coordinate transform (if there is one) // This should be optimised into other matrices (or even into // the geometry) when we perform the skeleton optimisation steps. topLevelPops = _skeleton.GetTransformationMachine().PushTransformation( coordinateTransform); } // When extracting an internal node, we ignore the transform // on that internal node BuildSkeleton(_skeleton, scene.GetRootNode(), rootNode, jointRefs, false); _skeleton.GetTransformationMachine().Pop(topLevelPops); // For each output matrix, we want to know if we can merge a transformation into it. // We can only do this if (unskinned) geometry instances are attached -- and those // geometry instances must be attached in only one place. If the output transform does // not have a geometry instance attached, or if any of the geometry instances are // attached to more than one matrix, or if something other than a geometry instance is // attached, then we cannot do any merging. TransMachineOptimizer optimizer(refGeos, _skeleton.GetTransformationMachine().GetOutputMatrixCount(), scene); _skeleton.GetTransformationMachine().Optimize(optimizer); // We can try to optimise the skeleton here. We should collect the list // of meshes that we can optimise transforms into (ie, meshes that aren't // used in multiple places, and that aren't skinned). // We need to collect that list of transforms before we actually instantiate // the geometry -- so that merging in the changes can be done in the instantiate // step. for (auto c:refGeos._meshes) { TRY { _cmdStream.Add( RenderCore::ColladaConversion::InstantiateGeometry( scene.GetInstanceGeometry(c._objectIndex), c._outputMatrixIndex, optimizer.GetMergedOutputMatrix(c._outputMatrixIndex), c._levelOfDetail, input._resolveContext, _geoObjects, jointRefs, input._cfg)); } CATCH(const std::exception& e) { LogWarning << "Got exception while instantiating geometry (" << scene.GetInstanceGeometry(c._objectIndex)._reference.AsString().c_str() << "). Exception details:"; LogWarning << e.what(); } CATCH(...) { LogWarning << "Got unknown exception while instantiating geometry (" << scene.GetInstanceGeometry(c._objectIndex)._reference.AsString().c_str() << ")."; } CATCH_END
void PushString( std::basic_streambuf<OutChar>& stream, StringSection<InChar> input) { stream.sputn((const OutChar*)input.begin(), input.Length()); }
void FileOutputStream::Write(StringSection<ucs4> s) { _file.Write(s.begin(), sizeof(*s.begin()), s.Length()); }