x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

No mipmap support for Dynamic Texture 2D

I'm trying to set material onto object at run-time from texture, normal map, displacement map etc. located in the hard disk. To achieve this i need to create dynamic material instance and set the parameter to the material by loading the image file at run-time.

I'm creating material from texture & displacement map image file. These image files are loading from folder using Victory Plugin's "Victory Load Texture 2D from File" Node.

I'm creating the material instance dynamic of the "test material". Now I'm setting the texture and displacement parameter as shown in figure 4. After creating material instance dynamic, I'm setting this material to the object.

figure[1] test material: http://imgur.com/PHlU0TA

figure[2] texture: http://imgur.com/6YmZ4ea

figure[3] displacement map: http://imgur.com/iGDYsrS

figure[4] Method: http://imgur.com/PQZ9C3a

Now here is the problem.

When I directly create material in material editor from texture and displacement map and set it onto object. It doesn't generate* moiré pattern.*

But when I run-time set the material onto object using figure [4]. It will generate moiré pattern.

Here are the sample screen shots of both.

figure[5]: Material Editor result: http://imgur.com/nKan9eK

figure[6]: Runtime result: http://imgur.com/ao2XnDk

I think the "Load Texture 2D from File" nodes is not calculating the Mip Map correctly.

I tried with following nodes but the result is same. 1> "Victory Load Texture 2D from File" (Created by rama) 2> "Load Texture 2D from File by Extension" (Created by you) 3> "Victory Load Texture 2D from File Pixels" (Created by rama)

http://imgur.com/MOZQLSu

For MipMap Calculation I modified the plugin file VictoryBPFunctionLibrary.cpp.

Here is the function that I'm using without any success.

UTexture2D* UVictoryBPFunctionLibrary::Victory_LoadTexture2D_FromFile(const FString& FullFilePath,EJoyImageFormats ImageFormat, bool& IsValid,int32& Width, int32& Height) {

 IsValid = false;
 UTexture2D* LoadedT2D = NULL;
 
 IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
 IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(GetJoyImageFormat(ImageFormat));
 
 //Load From File
 TArray<uint8> RawFileData;
 if (!FFileHelper::LoadFileToArray(RawFileData, *FullFilePath)) return NULL;
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
 //Create T2D!
 if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(RawFileData.GetData(), RawFileData.Num()))
 { 
     const TArray<uint8>* UncompressedBGRA = NULL;
     if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
     {
         LoadedT2D = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
         LoadedT2D->MipGenSettings=TMGS_FromTextureGroup;
         
         //Valid?
         if(!LoadedT2D) return NULL;
         //~~~~~~~~~~~~~~
         
         //Out!
         Width = ImageWrapper->GetWidth();
         Height = ImageWrapper->GetHeight();
          
         //Copy!
         void* TextureData = LoadedT2D->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
         FMemory::Memcpy(TextureData, UncompressedBGRA->GetData(), UncompressedBGRA->Num());
         LoadedT2D->PlatformData->Mips[0].BulkData.Unlock();

         //Update!
         LoadedT2D->UpdateResource();
     }
 }
  
 // Success!
 IsValid = true;
 return LoadedT2D;

}

I also tried with

LoadedT2D->MipGenSettings=TMGS_Blur1;

to

LoadedT2D->MipGenSettings=TMGS_Blur5;

LoadedT2D->MipGenSettings=TMGS_Sharpen0;

to

LoadedT2D->MipGenSettings=TMGS_Sharpen10;

LoadedT2D->MipGenSettings=TMGS_SimpleAverage;

After so many attempts result is same.

While playing the game when event occur, it closes the editor without any prompt or crash report.

I think engine is not able to set the mipmap at runtime.

Product Version: UE 4.12 Preview
Tags:
more ▼

asked May 24 '16 at 04:58 AM in C++ Programming

avatar image

manuaganu
231 10 19 25

avatar image Tim Hobson ♦♦ STAFF May 24 '16 at 09:00 PM

Hi Manuaganu, Can you reproduce this without the Victory Plugin or nodes from this plugin? If so I can look into this further. We do not support other users plugin's and bugs that may be generated because of those plugins. MipMaps cannot be set at runtime.

If you can reproduce please with the base editor please include the repro steps here, If not this question would likely be better handled in the C++ section where the community can offer assistance.

Thank you!

Tim

avatar image manuaganu May 25 '16 at 04:13 AM

Hi Tim,

Thank you for the reply. Actually my project is blueprint project. I'm novice in workflow of UE4 for c++ programming. So I have moved the question to C++ section. I hope someone can help here.

you have mentioned that "MipMaps cannot be set at runtime". Do you mean that it can not be possible to generate MipMap using C++ or Blueprint Programming ?

Regards,

Manuganu

avatar image Tim Hobson ♦♦ STAFF May 25 '16 at 01:06 PM

The texture mipmap settings cannot be adjusted at runtime. These are non-accessible for BP or C++ out of the box. However, I'm sure if you have some C++ savvy that can be adjusted, but it's not an area I'm familiar with in regards to programming.

avatar image langlangking Apr 23 '18 at 06:33 AM

Hi manuaganu, have you solved your problem?

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

Its possible to set mips at runtime but when I looked last year it wasn't possible to use the engine mip generation code as this was all private and not available during texture loading. Perhaps this has changed?

Even a simple averaged pixel algorithm will give you better results than no mips though - I've pasted one below. Note that you'll want square textures.

 auto workFunc = [texPtr, bThreaded]()
     {
         if (!texPtr.IsValid())
             return;
 
         auto *pNewTexture = texPtr.Get();
 
         int mipsToAdd = 3;// pNewTexture->RequestedMips - 1;
 
                           //Declaring buffers here to reduce reallocs
                           //We double buffer mips, using the prior buffer to build the next buffer
         TArray<uint8> _mipRGBAs;
         TArray<uint8> _mipRGBBs;
 
         //Access source data
         auto* priorData = (const uint8 *)pNewTexture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
         int priorwidth = pNewTexture->PlatformData->Mips[0].SizeX;
         int priorheight = pNewTexture->PlatformData->Mips[0].SizeY;
 
         while (mipsToAdd > 0)
         {
             auto *mipRGBAs = mipsToAdd & 1 ? &_mipRGBAs : &_mipRGBBs;
 
             int mipwidth = priorwidth >> 1;
             int mipheight = priorheight >> 1;
             if ((mipwidth == 0) || (mipheight == 0))
             {
                 break;
             }
             //++pNewTexture->RequestedMips;
 
             mipRGBAs->Reset();
             mipRGBAs->AddUninitialized(mipwidth * mipheight * BYTES_PER_PIXEL);
 
             int dataPerRow = priorwidth * BYTES_PER_PIXEL;
 
             //Average out the values
             auto *dataOut = mipRGBAs->GetData();
             for (int y = 0; y < mipheight; y++)
             {
                 auto *dataInRow0 = priorData + (dataPerRow * y * 2);
                 auto *dataInRow1 = dataInRow0 + dataPerRow;
                 for (int x = 0; x < mipwidth; x++)
                 {
                     int totalB = *dataInRow0++;
                     int totalG = *dataInRow0++;
                     int totalR = *dataInRow0++;
                     int totalA = *dataInRow0++;
                     totalB += *dataInRow0++;
                     totalG += *dataInRow0++;
                     totalR += *dataInRow0++;
                     totalA += *dataInRow0++;
 
                     totalB += *dataInRow1++;
                     totalG += *dataInRow1++;
                     totalR += *dataInRow1++;
                     totalA += *dataInRow1++;
                     totalB += *dataInRow1++;
                     totalG += *dataInRow1++;
                     totalR += *dataInRow1++;
                     totalA += *dataInRow1++;
 
                     totalB >>= 2;
                     totalG >>= 2;
                     totalR >>= 2;
                     totalA >>= 2;
 
                     *dataOut++ = (uint8)totalB;
                     *dataOut++ = (uint8)totalG;
                     *dataOut++ = (uint8)totalR;
                     *dataOut++ = (uint8)totalA;
                 }
                 dataInRow0 += priorwidth * 2;
                 dataInRow1 += priorwidth * 2;
             }
 
             // Allocate next mipmap.
             FTexture2DMipMap* Mip = new(pNewTexture->PlatformData->Mips) FTexture2DMipMap();
             Mip->SizeX = mipwidth;
             Mip->SizeY = mipheight;
             Mip->BulkData.Lock(LOCK_READ_WRITE);
             void* mipData = Mip->BulkData.Realloc(mipRGBAs->Num());
             FMemory::Memcpy(mipData, mipRGBAs->GetData(), mipRGBAs->Num());
             Mip->BulkData.Unlock();
 
             priorData = mipRGBAs->GetData();
             priorwidth = mipwidth;
             priorheight = mipheight;
             --mipsToAdd;
         }
 
         pNewTexture->PlatformData->Mips[0].BulkData.Unlock();
         if (bThreaded)
         {
             AppData::CallOnMainThread([texPtr] {
                 if (texPtr.IsValid())
                 {
                     texPtr->UpdateResource();
                 }
             });
         }
         else
         {
             pNewTexture->UpdateResource();
         }
     };
more ▼

answered Feb 05 '18 at 08:46 PM

avatar image

theonecalledtom
1.3k 60 64 269

avatar image theonecalledtom Feb 05 '18 at 08:49 PM

(CallOnMainThread is custom in my codebase, if you are not threading anything you don't need to worry about that its just that you need to call UpdateResource from the main thread and I shift my mip generation onto a background worker thread).

avatar image muchcharles Mar 29 '19 at 07:21 PM

Where you call this for each mip:

 FTexture2DMipMap* Mip = new(pNewTexture->PlatformData->Mips) FTexture2DMipMap();

Aren't you erasing the prior mip with each placement new in the loop? Shouldn't it be indexing into PlatformData->Mips? I'm not quite sure how TIndirectArray works, so maybe something else is changing it during the loop.

avatar image theonecalledtom Mar 29 '19 at 07:27 PM

That's what the engine code does - my assumption is that there is some sort of custom new handling but I didn't step into it. It's a good question though.

avatar image muchcharles Mar 29 '19 at 08:08 PM

ah thanks I'll check into that, TIndirectArray does seem to overload placement new

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question