Parece que a resposta certa para isso é pular o ContentPipeline e usar Texture2D.FromStream para carregar as texturas em tempo de execução. Esse método funciona bem em um PC e, mesmo com um pequeno impacto no desempenho, isso é algo que eu posso otimizar quando estiver mais próximo da data de lançamento. Por enquanto, ter a capacidade de modificar dinamicamente o conteúdo do editor e do jogo é exatamente o que eu preciso. Depois que o conteúdo estiver congelado, eu posso otimizar isso retornando ao ContentPipeline.
Como você escolheu essa rota, devo avisá-lo de que não é tão simples quanto usar apenas Texture2D.FromStream
por dois motivos:
Problema # 1 - Falta de suporte alfa pré-multiplicado
O XNA4 agora lida com texturas com cores no formato alfa pré-multiplicado por padrão. Quando você carrega uma textura através do pipeline de conteúdo, esse processamento é feito automaticamente para você. Infelizmente Texture2D.FromStream
, não faz o mesmo, portanto, qualquer textura que exija algum grau de transparência será carregada e renderizada incorretamente. Abaixo está uma captura de tela para ilustrar o problema:
Portanto, para obter os resultados corretos, você mesmo precisa fazer o processamento. O método que mostrarei usa a GPU para fazer o processamento, então é bem rápido. Foi baseado neste ótimo artigo . Claro que você também pode instruir SpriteBatch
para renderizar no antigo modo NonPremultiplyAlpha, mas eu realmente não recomendo fazer isso.
Problema # 2 - Formatos não suportados
O pipeline de conteúdo suporta mais formatos que Texture2D.FromStream
. Em particular, Texture2D.FromStream
suporta apenas png, jpg e gif. Por outro lado, o pipeline de conteúdo suporta bmp, dds, dib, hdr, jpg, pfm, png, ppm e tga. Se você tentar carregar um formato usuportado, Texture2D.FromStream
receberá um InvalidOperationException
pouco de informações adicionais.
Eu realmente precisava de suporte bmp no meu mecanismo, então, para esse caso em particular, encontrei uma solução alternativa que parece funcionar bem. Não conheço nenhum dos outros formatos. O problema com o meu método é que você precisa adicionar uma referência ao System.Drawing
assembly ao seu projeto, porque ele usa GDIs Image.FromStream
que suportam mais formatos do que Texture2D.FromStream
.
Se você não se importa com o suporte a bmp, pode facilmente largar essa parte da minha solução e fazer o processamento alfa pré-multiplicado.
Solução - Versão Simples (Mais Lenta)
Primeiro de tudo, aqui está a solução mais simples , se você não se importa com o suporte a bmps. Neste exemplo, o estágio de processamento é realizado inteiramente na CPU. É um pouco mais lento que a alternativa mostrada abaixo (fiz benchmark de ambas as soluções), mas é mais fácil de entender:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
texture.SetData(data);
return texture;
}
Se você se preocupa com bmps, o que você precisa fazer é carregar a imagem com o GDI primeiro e depois converter em PNG internamente antes de passá-la para Texture2D.FromStream
. Aqui está o código que faz isso:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(_graphicsDevice, ms);
}
}
Solução - Versão complexa (mais rápida)
Por fim, a abordagem usada em meus projetos é usar a GPU para fazer o processamento. Neste método, você precisa criar um destino de renderização, configurar corretamente alguns estados de mesclagem e desenhar a imagem duas vezes com um SpriteBatch. No final, reviso todo o RenderTarget2D e clono o conteúdo em um objeto Texture2D separado porque o RenderTarget2D é volátil e não sobrevive a coisas como alterar o tamanho do backbuffer para que seja mais seguro fazer uma cópia.
O engraçado é que, mesmo com tudo isso, nos meus testes essa abordagem foi executada cerca de três vezes mais rápido que a da CPU. Portanto, é definitivamente mais rápido do que passar por cima de cada pixel e calcular a cor você mesmo. O código é um pouco longo, então eu o coloquei em um pastebin:
http://pastie.org/3651642
Basta adicionar essa classe ao seu projeto e usá-la da maneira mais simples:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Nota: Você só precisa criar uma TextureLoader
instância para o jogo inteiro. Também estou usando a correção BMP, mas você pode removê-la se não precisar e obter um monte de desempenho ou apenas deixar o needsBmp
parâmetro como falso.
FromStream
um fluxo de memória contendo um bitmap de 32 bits (salvo em png), assim como no outro exemplo, mas esse método não criou uma textura pré-multiplicada. A pré-multiplicação explícita de cada cor fez o truque, obrigado.Eu acho que a maioria das equipes confirmará as alterações do xnb (bem, todas as mudanças realmente, inclusive o xnb) no servidor svn (que pode ser configurado gratuitamente) e permitirá que outras pessoas (o artista etc.) atualizem suas próprias cópias de trabalho.
De fato, esse seria um bom mecanismo para o artista controlar a versão original (pre-xnb) art. Ele iria confirmar as alterações, você atualizaria sua cópia de trabalho, construiria (tornando-o um xnb no processo), confirmará suas alterações, ele atualiza a cópia de trabalho do seu trabalho e todo mundo tem todas as alterações. (Você tem a última arte bruta, ele tem o xnb (s).
Isso escala muito bem também.
fonte
Continuei investigando isso e publicarei isso em benefício de alguém que tenha a mesma pergunta.
Parece que a resposta certa para isso é pular o ContentPipeline e usar Texture2D.FromStream para carregar as texturas em tempo de execução. Esse método funciona bem em um PC e, mesmo com um pequeno impacto no desempenho, isso é algo que eu posso otimizar quando estiver mais próximo da data de lançamento.
Por enquanto, ter a capacidade de modificar dinamicamente o conteúdo do editor e do jogo é exatamente o que eu preciso. Depois que o conteúdo estiver congelado, eu posso otimizar isso retornando ao ContentPipeline.
fonte
Texture2D.FromStream
por si só não é suficiente. A razão para isso é que, uma vez que a versão 4 do XNA trabalha com texturas alfa pré-multiplicadas, e enquanto o pipeline de conteúdo cuida desse processamento automaticamente para você,Texture2D.FromStream
isso não ocorre , portanto você provavelmente terá problemas ao desenhar sprites com transparência. Posso postar uma solução funcional, se você quiser.Texture2D.FromStream
não oferece suporte ao carregamento de.BMP
arquivos enquanto o pipeline de conteúdo suporta. Isso é algo que provavelmente o afastaria se você usasse algum.BMP
ativo anteriormente e depois o trocasseTexture2D.FromStream
. Eu também tenho uma solução alternativa para essa limitação. Vou apenas postar.Confira este projeto .
Isso permitirá que seu artista crie XNB a partir de praticamente qualquer coisa que o pipeline de conteúdo XNA padrão suporte. A estrutura XNA redistribuível ainda é necessária, embora seu artista não precise do Visual Studio.
fonte