Ejemplo n.º 1
0
def getWorldInfo(filename):
    worldAdapter = findAdapter(filename, readonly=True)
    try:
        displayNameLimit = 40
        name = displayName(worldAdapter.filename)

        if len(name) > displayNameLimit:
            name = name[:displayNameLimit] + "..."
        if usefulFilename(worldAdapter) != displayName(worldAdapter.filename):
            name = "%s (%s)" % (name, usefulFilename(worldAdapter))

        lastPlayed = lastPlayedTime(worldAdapter)
        lastPlayedText = lastPlayed.humanize() if lastPlayed else "Unknown"

        version = "Unknown Version"
        try:
            stackVersion = worldAdapter.blocktypes.itemStackVersion
            if stackVersion == VERSION_1_7:
                version = "Minecraft 1.7"
                if "FML" in worldAdapter.metadata.metadataTag:
                    version = "MinecraftForge 1.7"

            if stackVersion == VERSION_1_8:
                version = "Minecraft 1.8"
        except Exception as e:
            log.warn("Failed to get version info for %s: %r", filename, e)
        return name, lastPlayedText, version

    except Exception as e:
        log.error("Failed getting world info for %s: %r", filename, e)
        return str(e), "", ""
Ejemplo n.º 2
0
def getWorldInfo(filename):
    worldAdapter = findAdapter(filename, readonly=True)
    try:
        displayNameLimit = 40
        name = displayName(worldAdapter.filename)

        if len(name) > displayNameLimit:
            name = name[:displayNameLimit] + "..."
        if usefulFilename(worldAdapter) != displayName(worldAdapter.filename):
            name = "%s (%s)" % (name, usefulFilename(worldAdapter))

        lastPlayed = lastPlayedTime(worldAdapter)
        lastPlayedText = lastPlayed.humanize() if lastPlayed else "Unknown"

        version = "Unknown Version"
        try:
            stackVersion = worldAdapter.blocktypes.itemStackVersion
            if stackVersion == VERSION_1_7:
                version = "Minecraft 1.7"
                if "FML" in worldAdapter.metadata.metadataTag:
                    version = "MinecraftForge 1.7"

            if stackVersion == VERSION_1_8:
                version = "Minecraft 1.8"
        except Exception as e:
            log.warn("Failed to get version info for %s: %r", filename, e)
        return name, lastPlayedText, version

    except Exception as e:
        log.error("Failed getting world info for %s: %r", filename, e)
        return str(e), "", ""
Ejemplo n.º 3
0
    def updateRecentFilesMenu(self):
        recentFiles = RecentFilesSetting.value()
        recentWorldsMenu = self.mainWindow.menuRecent_Worlds
        for i, child in enumerate(recentWorldsMenu.children()):
            if i < 2:
                continue  # Skip "clear" and separator
            child.setParent(None)

        log.info("Updating recent files menu: (%d) %s", len(recentFiles), recentFiles)
        filenames = []
        displayNames = collections.Counter()
        for filename in recentFiles:
            text = util.displayName(filename)
            filenames.append((text, filename))
            displayNames[text] += 1

        displayFilenames = []
        for text, path in filenames:
            if displayNames[text] > 1:
                text += " (%s)" % path
            displayFilenames.append((text, path))

        for text, path in displayFilenames:
            log.info("Adding %s", text)
            action = recentWorldsMenu.addAction(text)

            def _triggered(p):
                def _f():
                    self.loadFile(p)
                return _f

            triggered = _triggered(path)
            action.triggered.connect(triggered)
            action.__triggered = triggered
Ejemplo n.º 4
0
 def __str__(self):
     if self.dimension:
         dimName = displayName(self.dimension.worldEditor.filename
                               ) + ": " + self.dimension.dimName
     else:
         dimName = "None"
     return "%s(%r)" % (self.__class__.__name__, dimName)
Ejemplo n.º 5
0
    def updateRecentFilesMenu(self):
        recentFiles = RecentFilesSetting.value()
        recentWorldsMenu = self.mainWindow.menuRecent_Worlds
        for i, child in enumerate(recentWorldsMenu.children()):
            if i < 2:
                continue  # Skip "clear" and separator
            child.setParent(None)

        log.info("Updating recent files menu: (%d) %s", len(recentFiles), recentFiles)
        filenames = []
        displayNames = collections.Counter()
        for filename in recentFiles:
            text = util.displayName(filename)
            filenames.append((text, filename))
            displayNames[text] += 1

        displayFilenames = []
        for text, path in filenames:
            if displayNames[text] > 1:
                text += " (%s)" % path
            displayFilenames.append((text, path))

        for text, path in displayFilenames:
            log.info("Adding %s", text)
            action = recentWorldsMenu.addAction(text)

            def _triggered(p):
                def _f():
                    self.loadFile(p)
                return _f

            triggered = _triggered(path)
            action.triggered.connect(triggered)
            action.__triggered = triggered
Ejemplo n.º 6
0
    def __init__(self,
                 world,
                 resourceLoader,
                 blockModels,
                 maxLOD=0,
                 overrideMaxSize=None):
        """
        Important members:

            textureData: RGBA Texture Data as a numpy array.

            texCoordsByName: Dictionary of texture coordinates. Usable for textures loaded using the extraTextures argument
                or from block definitions.
                Maps "texture_name" -> (left, top, right, bottom)


        :param world:
        :type world: mceditlib.worldeditor.WorldEditor
        :param resourceLoader:
        :type resourceLoader: mcedit2.resourceloader.ResourceLoader
        :param blockModels:
        :type blockModels: mcedit2.rendering.blockmodels.BlockModels
        :param maxLOD: Adds wrapped borders to each texture to allow mipmapping at this level of detail
        :type maxLOD: int
        :param overrideMaxSize: Override the maximum texture size - ONLY use for testing TextureAtlas without creating a GL context.
        :type overrideMaxSize: int or None
        :return:
        :rtype: TextureAtlas
        """
        self.overrideMaxSize = overrideMaxSize
        self.blockModels = blockModels
        self.blocktypes = world.blocktypes
        self._filename = world.filename
        self._resourceLoader = resourceLoader
        self._lightTexture = None
        self._terrainTexture = None
        self._maxLOD = maxLOD

        names = set()
        self._rawTextures = rawTextures = []
        assert "MCEDIT_UNKNOWN" in blockModels.getTextureNames()
        for filename in blockModels.getTextureNames():
            if filename in names:
                continue
            try:
                f = self._openImageStream(filename)
                rawTextures.append((filename, ) + loadPNGData(f.read()))
                names.add(filename)
                log.debug("Loaded texture %s", filename)
            except ResourceNotFound as e:
                log.error("Could not load texture %s: %r", filename, e)
            except Exception as e:
                log.exception("%s while loading texture '%s', skipping...", e,
                              filename)

        rawSize = sum(a.nbytes for (n, w, h, a) in rawTextures)

        log.info("Preloaded %d textures for world %s (%i kB)",
                 len(self._rawTextures), util.displayName(self._filename),
                 rawSize / 1024)
Ejemplo n.º 7
0
def getWorldInfo(filename):
    worldAdapter = findAdapter(filename, readonly=True)
    try:
        displayNameLimit = 40
        name = displayName(worldAdapter.filename)

        if len(name) > displayNameLimit:
            name = name[:displayNameLimit] + "..."
        if usefulFilename(worldAdapter) != displayName(worldAdapter.filename):
            name = "%s (%s)" % (name, usefulFilename(worldAdapter))

        lastPlayed = lastPlayedTime(worldAdapter)
        lastPlayedText = lastPlayed.humanize() if lastPlayed else "Unknown"
        return name, lastPlayedText
    except EnvironmentError as e:
        log.error("Failed getting world info for %s: %s", filename, e)
        return str(e), ""
Ejemplo n.º 8
0
def getWorldInfo(filename):
    worldAdapter = findAdapter(filename, readonly=True)
    try:
        displayNameLimit = 40
        name = displayName(worldAdapter.filename)

        if len(name) > displayNameLimit:
            name = name[:displayNameLimit] + "..."
        if usefulFilename(worldAdapter) != displayName(worldAdapter.filename):
            name = "%s (%s)" % (name, usefulFilename(worldAdapter))

        lastPlayed = lastPlayedTime(worldAdapter)
        lastPlayedText = lastPlayed.humanize() if lastPlayed else "Unknown"
        return name, lastPlayedText
    except EnvironmentError as e:
        log.error("Failed getting world info for %s: %s", filename, e)
        return str(e), ""
Ejemplo n.º 9
0
 def __str__(self):
     try:
         if self.dimension:
             dimName = displayName(self.dimension.worldEditor.filename) + ": " + self.dimension.dimName
         else:
             dimName = "None"
     except Exception as e:
         return "%s trying to get node name" % e
     return "%s(%r)" % (self.__class__.__name__, dimName)
Ejemplo n.º 10
0
    def __init__(self, world, resourceLoader, blockModels, maxLOD=0, overrideMaxSize=None):
        """
        Important members:

            textureData: RGBA Texture Data as a numpy array.

            texCoordsByName: Dictionary of texture coordinates. Usable for textures loaded using the extraTextures argument
                or from block definitions.
                Maps "texture_name" -> (left, top, right, bottom)


        :param world:
        :type world: mceditlib.worldeditor.WorldEditor
        :param resourceLoader:
        :type resourceLoader: mcedit2.resourceloader.ResourceLoader
        :param blockModels:
        :type blockModels: mcedit2.rendering.blockmodels.BlockModels
        :param maxLOD: Adds wrapped borders to each texture to allow mipmapping at this level of detail
        :type maxLOD: int
        :param overrideMaxSize: Override the maximum texture size - ONLY use for testing TextureAtlas without creating a GL context.
        :type overrideMaxSize: int or None
        :return:
        :rtype: TextureAtlas
        """
        self.overrideMaxSize = overrideMaxSize
        self.blockModels = blockModels
        self.blocktypes = world.blocktypes
        self._filename = world.filename
        self._resourceLoader = resourceLoader
        self._lightTexture = None
        self._terrainTexture = None
        self._maxLOD = maxLOD


        names = set()
        self._rawTextures = rawTextures = []
        assert "MCEDIT_UNKNOWN" in blockModels.getTextureNames()
        for filename in blockModels.getTextureNames():
            if filename in names:
                continue
            try:
                f = self._openImageStream(filename)
                rawTextures.append((filename,) + loadPNGData(f.read()))
                names.add(filename)
                log.debug("Loaded texture %s", filename)
            except ResourceNotFound as e:
                log.error("Could not load texture %s: %r", filename, e)
            except Exception as e:
                log.exception("%s while loading texture '%s', skipping...", e, filename)

        rawSize = sum(a.nbytes for (n, w, h, a) in rawTextures)

        log.info("Preloaded %d textures for world %s (%i kB)",
                 len(self._rawTextures), util.displayName(self._filename), rawSize/1024)
Ejemplo n.º 11
0
    def updateRecentFilesMenu(self):
        recentFiles = RecentFilesSetting.value()
        for filename in recentFiles:
            text = util.displayName(filename)
            action = self.recentFilesMenu.addAction(text)

            def _triggered():
                self.loadFile(filename)

            action.triggered.connect(_triggered)
            action.__triggered = _triggered
Ejemplo n.º 12
0
    def __init__(self, worldAdapter, parent=None):
        QtGui.QPushButton.__init__(self, parent)
        self.filename = worldAdapter.filename
        self.setCheckable(True)
        self.setFlat(True)

        try:
            # mapLabel = QtGui.QLabel()
            # mapLabel.setFixedSize(72, 72)
            displayNameLimit = 50
            name = displayName(worldAdapter.filename)
            if len(name) > displayNameLimit:
                name = name[:displayNameLimit] + "..."
            self.displayNameLabel = QtGui.QLabel(name)
            self.lastPlayed = lastPlayedTime(worldAdapter)
            lastPlayedText = self.lastPlayed.humanize(
            ) if self.lastPlayed else "Unknown"
            self.lastPlayedLabel = QtGui.QLabel(lastPlayedText)
            #self.sizeLabel = QtGui.QLabel(self.tr("Calculating area..."))
            # areaText = self.tr("%.02f million square meters") % (world.chunkCount * 0.25)
            # diskSize = 0
            # if hasattr(worldAdapter, 'worldFolder'):
            #     folder = worldAdapter.worldFolder
            #     for rf in folder.findRegionFiles():
            #         diskSize += os.stat(rf).st_size
            # else:
            #     diskSize = os.stat(worldAdapter.filename).st_size
            #
            # self.diskSizeLabel = QtGui.QLabel(self.tr("%0.2f MB") % (diskSize / 1000000.0))

            infoColumn = Column(
                self.displayNameLabel,
                self.lastPlayedLabel,
                #self.diskSizeLabel,
                None)

            #layout = Row(mapLabel, (infoColumn, 1), None)
            layout = infoColumn
            #if usefulFilename(world) == world.displayName:
            #    boxLabelText = world.displayName
            #else:
            #    boxLabelText = self.tr("%s (%s)" % (world.displayName, usefulFilename(world)))

            self.setLayout(layout)
            self.setMinimumSize(layout.sizeHint())
            self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding,
                               QtGui.QSizePolicy.MinimumExpanding)

        except EnvironmentError as e:
            setWidgetError(self, e)
Ejemplo n.º 13
0
    def load(self):
        if self._terrainTexture is not None:
            return
        maxLOD = min(4, self._maxLOD)

        if self.overrideMaxSize is None:
            if maxLOD:
                minFilter = GL.GL_NEAREST_MIPMAP_LINEAR
            else:
                minFilter = None
            self._terrainTexture = glutils.Texture(name="TextureAtlas",
                                                   image=self.textureData.ravel(),
                                                   width=self.width, height=self.height,
                                                   minFilter=minFilter, maxLOD=maxLOD)
            self._terrainTexture.load()
        else:
            self._terrainTexture = object()

        if self._lightTexture is None:
            self._lightTexture = LightTexture(self.dayTime, self.minBrightness)
            self._lightTexture.load()

        log.info("GL resources loaded for TextureAtlas for %s", util.displayName(self._filename))
Ejemplo n.º 14
0
 def displayName(self):
     return displayName(self.filename)
Ejemplo n.º 15
0
    def createAtlasImage(self):
        if self.textureData is not None:
            return

        if self.overrideMaxSize is None:
            maxSize = getGLMaximumTextureSize()
        else:
            maxSize = self.overrideMaxSize

        maxLOD = min(4, self._maxLOD)
        if maxLOD:
            borderSize = 1 << (maxLOD - 1)
        else:
            borderSize = 0

        slots = []
        atlasWidth = 0
        atlasHeight = 0
        self._rawTextures.sort(key=lambda (_, w, h, __): max(w, h), reverse=True)

        for path, w, h, data in self._rawTextures:
            w += borderSize * 2
            h += borderSize * 2
            for slot in slots:
                if slot.addTexture(path, w, h, data):
                    log.debug("Slotting %s into an existing slot", path)
                    break
            else:
                if atlasHeight < 24 * atlasWidth and atlasHeight + h < maxSize:
                    # Prefer to lay out textures vertically, since animations are vertical strips
                    slots.append(TextureSlot(0, atlasHeight, max(atlasWidth, w), atlasHeight + h))
                    atlasWidth = max(atlasWidth, w)
                    atlasHeight = atlasHeight + h
                else:
                    slots.append(TextureSlot(atlasWidth, 0, atlasWidth + w, max(atlasHeight, h)))
                    atlasWidth = atlasWidth + w
                    atlasHeight = max(atlasHeight, h)

                if atlasWidth > maxSize or atlasHeight > maxSize:
                    raise ValueError("Building texture atlas: Textures too large for maximum texture size. (Needed "
                                     "%s, only got %s", (atlasWidth, atlasHeight), (maxSize, maxSize))

                if not slots[-1].addTexture(path, w, h, data):
                    raise ValueError("Building texture atlas: Internal error.")

                log.debug("Slotting %s into a newly created slot", path)

        self.width = atlasWidth
        self.height = atlasHeight

        self.textureData = texData = numpy.zeros((atlasHeight, atlasWidth, 4), dtype='uint8')
        self.textureData[:] = [0xff, 0x0, 0xff, 0xff]
        b = borderSize
        for slot in slots:
            for name, left, top, width, height, data in slot.textures:
                log.debug("Texture %s at (%d,%d,%d,%d)", name, left, top, width, height)
                texDataView = texData[top:top + height, left:left + width]
                if b:
                    texDataView[b:-b, b:-b] = data

                    # Wrap texture edges to avoid antialiasing bugs at edges of blocks
                    texDataView[-b:, b:-b] = data[:b]
                    texDataView[:b, b:-b] = data[-b:]

                    texDataView[:, -b:] = texDataView[:, b:2 * b]
                    texDataView[:, :b] = texDataView[:, -b * 2:-b]
                else:
                    texDataView[:] = data
                self.texCoordsByName[name] = left + b, top + b, width - 2 * b, height - 2 * b

        totalSize = self.width * self.height * 4
        usedSize = sum(sum(width * height for _, _, _, width, height, _ in slot.textures) for slot in slots) * 4
        log.info("Terrain atlas created for world %s (%d/%d kB)", util.displayName(self._filename), usedSize / 1024,
                 totalSize / 1024)

        self.blockModels.cookQuads(self)
Ejemplo n.º 16
0
 def __str__(self):
     if self.dimension:
         dimName = displayName(self.dimension.worldEditor.filename) + ": " + self.dimension.dimName
     else:
         dimName = "None"
     return "%s(%r)" % (self.__class__.__name__, dimName)
Ejemplo n.º 17
0
 def tabCaption(self):
     return util.displayName(self.filename)
Ejemplo n.º 18
0
 def __str__(self):
     return "%s(%r)" % (self.__class__.__name__,
                        displayName(self.dimension.worldEditor.filename))
Ejemplo n.º 19
0
    def load(self):
        if self._terrainTexture:
            return

        if self.overrideMaxSize is None:
            maxSize = getGLMaximumTextureSize()
        else:
            maxSize = self.overrideMaxSize

        maxLOD = min(4, self._maxLOD)
        if not bool(GL.glGenerateMipmap):
            maxLOD = 0
        if maxLOD:
            borderSize = 1 << (maxLOD - 1)
        else:
            borderSize = 0

        slots = []
        atlasWidth = 0
        atlasHeight = 0
        self._rawTextures.sort(key=lambda (_, w, h, __): max(w, h), reverse=True)

        for name, w, h, data in self._rawTextures:
            w += borderSize * 2
            h += borderSize * 2
            for slot in slots:
                if slot.addTexture(name, w, h, data):
                    log.debug("Slotting %s into an existing slot", name)
                    break
            else:
                if atlasHeight < 24 * atlasWidth and atlasHeight + h < maxSize:
                    # Prefer to lay out textures vertically, since animations are vertical strips
                    slots.append(TextureSlot(0, atlasHeight, max(atlasWidth, w), atlasHeight + h))
                    atlasWidth = max(atlasWidth, w)
                    atlasHeight = atlasHeight + h
                else:
                    slots.append(TextureSlot(atlasWidth, 0, atlasWidth + w, max(atlasHeight, h)))
                    atlasWidth = atlasWidth + w
                    atlasHeight = max(atlasHeight, h)

                if atlasWidth > maxSize or atlasHeight > maxSize:
                    raise ValueError("Building texture atlas: Textures too large for maximum texture size. (Needed "
                                     "%s, only got %s", (atlasWidth, atlasHeight), (maxSize, maxSize))

                if not slots[-1].addTexture(name, w, h, data):
                    raise ValueError("Building texture atlas: Internal error.")

                log.debug("Slotting %s into a newly created slot", name)

        self.textureData = texData = numpy.zeros((atlasHeight, atlasWidth, 4), dtype='uint8')
        self.textureData[:] = [0xff, 0x0, 0xff, 0xff]
        self.texCoordsByName = {}
        b = borderSize
        for slot in slots:
            for name, left, top, width, height, data in slot.textures:
                log.debug("Texture %s at (%d,%d,%d,%d)", name, left, top, width, height)
                texDataView = texData[top:top + height, left:left + width]
                if b:
                    texDataView[b:-b, b:-b] = data

                    # Wrap texture edges to avoid antialiasing bugs at edges of blocks
                    texDataView[-b:, b:-b] = data[:b]
                    texDataView[:b, b:-b] = data[-b:]

                    texDataView[:, -b:] = texDataView[:, b:2 * b]
                    texDataView[:, :b] = texDataView[:, -b * 2:-b]
                else:
                    texDataView[:] = data
                self.texCoordsByName[name] = left + b, top + b, width - 2 * b, height - 2 * b

        def _load():
            GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, atlasWidth, atlasHeight, 0, GL.GL_RGBA,
                            GL.GL_UNSIGNED_BYTE, self.textureData.ravel())

        if self.overrideMaxSize is None:
            if maxLOD:
                minFilter = GL.GL_NEAREST_MIPMAP_LINEAR
            else:
                minFilter = None
            self._terrainTexture = glutils.Texture(_load, minFilter=minFilter, maxLOD=maxLOD)
            self._terrainTexture.load()
        else:
            self._terrainTexture = object()

        self.width = atlasWidth
        self.height = atlasHeight

        totalSize = atlasWidth * atlasHeight * 4
        usedSize = sum(sum(width * height for _, _, _, width, height, _ in slot.textures) for slot in slots) * 4
        log.info("Terrain atlas created for world %s (%d/%d kB)", util.displayName(self._filename), usedSize / 1024,
                 totalSize / 1024)
Ejemplo n.º 20
0
 def displayName(self):
     return displayName(self.filename)
Ejemplo n.º 21
0
 def tabCaption(self):
     return util.displayName(self.filename)
Ejemplo n.º 22
0
    def load(self):
        if self._terrainTexture:
            return

        if self.overrideMaxSize is None:
            maxSize = getGLMaximumTextureSize()
        else:
            maxSize = self.overrideMaxSize

        maxLOD = min(4, self._maxLOD)
        if not bool(GL.glGenerateMipmap):
            maxLOD = 0
        if maxLOD:
            borderSize = 1 << (maxLOD - 1)
        else:
            borderSize = 0

        slots = []
        atlasWidth = 0
        atlasHeight = 0
        self._rawTextures.sort(key=lambda (_, w, h, __): max(w, h),
                               reverse=True)

        for name, w, h, data in self._rawTextures:
            w += borderSize * 2
            h += borderSize * 2
            for slot in slots:
                if slot.addTexture(name, w, h, data):
                    log.debug("Slotting %s into an existing slot", name)
                    break
            else:
                if atlasHeight < 24 * atlasWidth and atlasHeight + h < maxSize:
                    # Prefer to lay out textures vertically, since animations are vertical strips
                    slots.append(
                        TextureSlot(0, atlasHeight, max(atlasWidth, w),
                                    atlasHeight + h))
                    atlasWidth = max(atlasWidth, w)
                    atlasHeight = atlasHeight + h
                else:
                    slots.append(
                        TextureSlot(atlasWidth, 0, atlasWidth + w,
                                    max(atlasHeight, h)))
                    atlasWidth = atlasWidth + w
                    atlasHeight = max(atlasHeight, h)

                if atlasWidth > maxSize or atlasHeight > maxSize:
                    raise ValueError(
                        "Building texture atlas: Textures too large for maximum texture size. (Needed "
                        "%s, only got %s", (atlasWidth, atlasHeight),
                        (maxSize, maxSize))

                if not slots[-1].addTexture(name, w, h, data):
                    raise ValueError("Building texture atlas: Internal error.")

                log.debug("Slotting %s into a newly created slot", name)

        self.textureData = texData = numpy.zeros((atlasHeight, atlasWidth, 4),
                                                 dtype='uint8')
        self.textureData[:] = [0xff, 0x0, 0xff, 0xff]
        self.texCoordsByName = {}
        b = borderSize
        for slot in slots:
            for name, left, top, width, height, data in slot.textures:
                log.debug("Texture %s at (%d,%d,%d,%d)", name, left, top,
                          width, height)
                texDataView = texData[top:top + height, left:left + width]
                if b:
                    texDataView[b:-b, b:-b] = data

                    # Wrap texture edges to avoid antialiasing bugs at edges of blocks
                    texDataView[-b:, b:-b] = data[:b]
                    texDataView[:b, b:-b] = data[-b:]

                    texDataView[:, -b:] = texDataView[:, b:2 * b]
                    texDataView[:, :b] = texDataView[:, -b * 2:-b]
                else:
                    texDataView[:] = data
                self.texCoordsByName[
                    name] = left + b, top + b, width - 2 * b, height - 2 * b

        def _load():
            GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, atlasWidth,
                            atlasHeight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
                            self.textureData.ravel())

        if self.overrideMaxSize is None:
            if maxLOD:
                minFilter = GL.GL_NEAREST_MIPMAP_LINEAR
            else:
                minFilter = None
            self._terrainTexture = glutils.Texture(_load,
                                                   minFilter=minFilter,
                                                   maxLOD=maxLOD)
            self._terrainTexture.load()
        else:
            self._terrainTexture = object()

        self.width = atlasWidth
        self.height = atlasHeight

        totalSize = atlasWidth * atlasHeight * 4
        usedSize = sum(
            sum(width * height for _, _, _, width, height, _ in slot.textures)
            for slot in slots) * 4
        log.info("Terrain atlas created for world %s (%d/%d kB)",
                 util.displayName(self._filename), usedSize / 1024,
                 totalSize / 1024)