Nuke Python 回调函数

使用下文描述的nuke.add...()函数,当有变量事件(比如,创建节点,加载脚本)时就自动调用python函数。

在init.py,menu.py里面可以随便用nuke.add...(). 这代码会被当作nuke环境的一部分,不跟着脚本变。

所有nuke.add...()函数的参数都一样, 例如:

nuke.addOnCreate( callable, args=(), kwargs={}, nodeClass='*' )

其中:

  • callable 是python调用,比如函数名之类

  • args 是参数列表,有圆括号包括着。比如:

    nuke.addOnCreate( nuke.tprint, ( 'what to print' ))

  • kwargs是用k-v形式给的参数,比如:

    nuke.addOnCreate( nuke.tprint, ('a', 'b'), {'sep':',' } )

  • nodeClass 仅在nuke.thisNode().nodeClass() 等于这个字符串时才调用,比如:

    nuke.addOnCreate( nuke.tprint, ('Creating Write'), nodeClass='Write')

默认的*意味着可以被任何节点调用。

在callback中,有个context节点,使用nuke.thisNode()时需要检验。knobChanged,有一个可以利用nuke.thisKonb()获取的内容knob。

对于众多的nuke.add...() 函数,比如 onCreate,也有同名的knob,其分成两类:

  • 可见knob-- 就是制作人员想编辑的knob。可以在属性panel的python面板看到。可见konb包括: onScriptLoad, onScriptSave, onScriptClose, beforeRender, beforeFrameRender, afterRender, afterFrameRender.

  • 隐藏knob 在属性面板看不到他们,但可以用python来设置。允许定义gizmo的行为。制作人员不应该设置这些knob,因为用户设置会覆盖gizmo设置。隐藏knob包括: onCreate, onDestroy, knobChanged, updateUI, autolabel.

如果很多注册的回调,附加到knob上的代码总是优先于nuke.add...()( 例如,放入到onCreate knob的代码,要比nuke.addOnCreate()先调用)大多数情况下,nuke.add...()函数都会按顺序跟着一个对应的nodeClass。最后,带* 的,也是添加的顺序。注意addAutolabel()和addFilenameFilter()函数是例外,他们按照添加时候的逆序调用。

所有的回调函数都有对应的移除函数


OnUserCreate
nuke.addOnUserCreate( function )
nuke.removeOnUserCreate( function )

当用户在GUI模式下创建节点时就执行。启动时在root和viewer节点上也会调用。但是加载现有脚本,粘贴节点,undo delete时,不会执行。

可以用这个代码改变knob的默认值,添加user knob,执行其他行为。
nuke.addOnUserCreate 在默认值改变后立即运行。要在onCreate前面。如果没有使用nuke.addOnUserCreate(),那就会调用nuke.tcl("OnCreate" )后向兼容。


onCreate
node.knob("onCreate" )
nuke.addOnCreate(function)
nuke.removeCreate(function)

当node创建时就执行,例如,当加载脚本,粘贴节点,选择菜单选项,撤销删除等。注意,如果onCreate在脚本加载前就定义了,会为每个节点执行。这是因为节点
创建出来后,脚本也已经加载了。 创建group时(包括root节点,包含很多内部节点),内部节点先调用onCreate,group节点后调用onCreate。root节点任何脚本
加载时都会执行(查看onScriptLoad) 或 菜单中File》New被选择时也会执行。 预设和python knob panel创建时也会执行。

例如,下面的代码让每个节点创建时在终端输出OnCreate。

nuke.addOnCreate( lambda: nuke.tprint(" OnCreate called for " + nuke.thisNode().name() ))
onScriptLoad
root.knob('onScriptLoad'), nuke.addOnScriptLoad( function), nuke.removeOnScriptLoad(function)


onScriptLoad
root.knob('onScriptLoad')
nuke.addOnScriptLoad( function )
nuke.removeOnScriptLoad( function )

当脚本加载时就执行,在root的onCreate创建后立即执行。注意,其不为新脚本执行。
在Project Setting》Python 标签可以看到 onScriptLoad的knob

image
image


onScriptSave
root.knob(' onSrciptSave')
nuke.addOnScriptSave( function)
nuke.removeOnScriptSave( function )

当用户保存脚本时运行。


image
image


onScriptClose
root.knob('onScriptClose')
nuke.addOnScriptClose( function )
nuke.removeOnScriptClose( function )

当用户退出nuke或者关闭脚本,scriptClear()函数


image
image


onDestroy
node.knob('onDestroy')
nuke.addOnDestroy( function )
nuke.removeOnDestroy( function )

节点删除时会执行此函数,包括撤销节点的创建。root节点关闭后也会调用,用户退出nuke,nuke崩溃后不会为Preferences。
对于group和root 此函数比所有孩子都要先运行。


knobChanged
node.knob('knobChanged')
nuke.addKnobChanged( function )
nuke.removeKnobChanged( function )

panel打开时,任何对knob数值的修改都能执行回调,不会递归调用。使用nuke.thisKnob()来查看哪个knob被改变了。
knobChanged可以制作带有knob的gizmos,knob有enabling,disabling,presettin等。nuke.thisKnob()能获取变动的那个knob。

注意:knobChanged是用来刷新panel控制面板。不能用来跟踪knobs(例如,用数据库的数据来更新所有的knobs)。因为当Properties面板关闭时,就不调用了。想跟踪knobs,用下面的updateUI

用户打开、关闭properties面板时,panel打开状态下,变动了input的连接,就可以使用knobChanged中的showPanelinputChange 这两个knobs。否则on change回调更改了knob的值,就要响应showPanel来设置正确的状态了。例如:

# if slider is zero, disable the checkmark:
n = nuke.thisNode()
k = nuke.thisKnob()
if k.name()=="slider" or k.name()=="showPanel":
  n['checkmark'].setEnable(n['slider'].getValue()!=0)

nuke.addKnobChanged 可以一次获取ColorCorrect节点的gain和gamma 滑动条,用户更改了其中一个滑动条,另一个会自动变为同样的值。获取滑动条的代码:

def gangGammaGainSliders():
 n = nuke.thisNode()
 k = nuke.thisKnob()
 if k.name() == "gamma":
   n['gain'].setValue(k.value())
 elif k.name() == "gain":
   n['gamma'].setValue(k.value())
nuke.addKnobChanged(gangGammaGainSliders, nodeClass="ColorCorrect")


updateUI
node.knob( 'updateUI')
nuke.addUpdateUI( function )
nuke.removeUpdateUI( function )

脚本变动后,每个节点都运行此回调。这个是作为空闲时间执行的低优先级进程来实现的。很可能Nuke都开始给view渲染计算了,updateUI还没执行呢。因此updateUI不能执行更改图像的脚本(否则,viewer就要重启了)。

updateUIautolabel之前knobChanged之后运行。想不让它在每个节点运行,Nuke检查updateUI看上去像啥(比如,帧,view数量)来决定是否运行它。 如果函数推测不出来帧和view数量,Nuke仅在这个node上knob变动时调用一次,frame或view变动不会调用。


autolabel
node.knob('autolabel')
nuke.addAutolabel( function )
nuke.removeAutolabel( function )

updateUI后立马运行上面脚本,并返回绘制节点的绘制字符串。如果autolabel不是空的,或返回任何非空,返回值就显示在节点上。否则,nuke.addAutolabel()会被逆序调用,使用第一个返回非None的。如果都返回None,那就用nuke.thisNode().name().

为了后向兼容,Nuke调用启动时调用addAutolabel(main.autolabel)


beforeRender
write.knob('beforeRender')
nuke.addBeforeRender( function )

这个在execute()开始渲染之前就运行了。抛出异常,就不进行后续渲染了。可以用这个特性来创建目标输出文件夹或者和农场通信,例如,beforeRender knob(GUI上是before render)就在write节点的Render页面上。

提示:默认情况下,NUKE渲染时不创建文件夹。如果Write节点的输出路径所在文件夹不存在,渲染会失败。但是,能通过nuke.addBeforeRender()注册回调函数渲染之前创建文件夹来改变nuke的默认行为。 下面是一个例子:

  1. 插件目录中创建 init.py 如果不存在
  2. 文件中添加如下代码:
def createWriteDir():
    import nuke, os, errno
    file = nuke.filename( nuke.thisNode() )
    dir = os.path.dirname( file )
    osdir = nuke.callbacks.filenameFilter( dir )
    try:
    os.makedirs( osdir )
    except OSError, e:
    if e.errno != errno.EEXIST:
    raise
    nuke.addBeforeRender( createWriteDir )
image
image


beforeFrameRender
write.knob('beforeFrameRender')
nuke.addBeforeFrameRender( function )
nuke.removeBeforeFrameRender(function )

每帧开始渲染前执行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,beforeFrameRender knob (GUI上的 after render)就在输出节点的Render页面下。

image
image


afterFrameRender
write.knob('afterFrameRender')
nuke.addAfterFrameRender( function)
nuke.removeAfterFrameRender( function )

每帧渲染完成后执行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,afterFrameRender knob (GUI上的 after render)就在输出节点的Render页面下。

image
image


afterRender
write.knob('afterRender')
nuke.addAfterRender( function )
nuke.removeAfterRender( function )

所有帧渲染完成后运行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,afterRender knob (GUI上的 after render)就在输出节点的Render页面下。

image
image


afterBackgroundRender
nuke.addAfterBackgroundRender( function )
nuke.removeAfterBackgroundRender( function )

后台渲染后执行,调用形式必须这样:

def foo( context ):
    pass

context是一个包含下面元素的字典:

  • id -- 完成task的标识

请注意当前NUKE上下文对回调无效,例如,nuke.thisNode会随机返回一个节点。


afterBackgroundFrameRender
nuke.addBackgroundFrameRender( function )
nuke.removeAfterBackgroundFrameRender( function )

调用形式如下:

def foo( context ):
    pass

context是一个包含下面元素的字典:

  • id -- 完成task的标识
  • frame -- 当前渲染的帧号
  • numFrames -- 要渲染的总帧数
  • framgeProgress -- 已经渲染了多少帧

请注意当前NUKE上下文对回调无效,例如,nuke.thisNode会随机返回一个节点。


filenameFilter
nuke.addFilenameFilter( function )
nuke.removeFilenameFilter( function )

在脚本传输给系统前,给过程添加一个过滤函数来处理文件名。
为了后向兼容,如果没有添加函数,nuke运行mian.filenameFix(s). 默认版本调用nuke.tcl('filename_fix',s)
使用这个来进行路径转换,从windows到linux的转换:

添加了一个filter在Nuke脚本传输到操作系统前处理所有的文件名。这些filter能消除操作系统间的差异,也能插入需要的路径。回调函数的参数必须是一个字符串,返回值可以是字符串也可以是None(这个返回没改动的字符串是一样的)。文件名并不是一个特殊的节点,比如插件名,此函数调用nuke.thisNode()设置root节点。所有传给nuke.addFilenameFilter的函数逆序调用。

为了后向兼容,如果没有添加函数,nuke运行main.filenameFix(s) 。默认版本调用nuke.tcl('filename_fix',s)

下面的例子使用filename filter来处理win和linux混合环境下的路径问题。例如,工作站是win系统,访问共享的驱动器‘Y:’,在linux对应的挂载点是‘/mnt/y/’:

import nuke

def myFilenameFilter(filename):
    if nuke.env['LINUX']:
        filename = filename.replace( 'y:', '/mnt/y' )
        filename = filename.replace( 'x:', '/mnt/x' )
    else:
        filename = filename.replace( '/mnt/y', 'y:' )
        filename = filename.replace( '/mnt/x', 'x:' )

    return filename

nuke.addFilenameFilter(myFilenameFilter)


validateFilename
nuke.addValidateFilename( function )
nuke.removeValidateFilename( function )

验证write node中的文件名。第一个参数是文件名,返回布尔值,说明文件名是否有效。如果提供了回调函数,Write node中的Render按钮,WriteGeo节点中的Excute按钮可用与否就受回调函数结果影响。


autoSaveFilter
nuke.addAutoSaveFilter( function )
nuke.removeAutosaveFilter( function )

在nuke自动保存文件文件前,对autosave进行修改。

def myAutoSaveFilter( filename ):
return filename

第一个参数为当前autosave文件名,返回的是要保存的aotusave文件名,木有就返回None。

更多请看 使用AutoSave回调实现AutoSave回滚


autoSaveRestoreFilter
nuke.addAutoSaveRestoreFilter( function )
nuke.removeAutoSaveRestore( function )

Nuke恢复aotosave文件时修改autosave获取的文件名

函数格式如下:

def myAutoSaveRestoreFilter( filename ):
    return filename

第一个参数为当前autosave 文件名,函数返回要加载的autosave文件名,没有返回None。如果aotosave恢复filter返回None这也压制了Nuke对话框询问用户是否加载一个找到的autosave。

更多请看 使用AutoSave回调实现AutoSave回滚


autoSaveDeleteFilter

在nuke删除自动保存文件时修改autosave 的文件名

filter函数形式如下:

def myAutoSaveDeleteFilter( filename ):
    return filename

第一个参数就是autosave的文件名。返回要删除的文件名,没有可删除的就返回None。

更多请看 使用AutoSave回调实现AutoSave回滚


使用autosave的回调完成autosave回滚操作

使用三个autosave回调来完成回滚。每次调用autosave,就创建一个新的autosave,数组从1-9.例如:autosave, autosave1, autosave2....autosave9

import nuke
import glob
import time
import os

### Example that implements a rolling autosave using the autoSaveFilter callbacks
###
## autosaves roll from 0-9 eg myfile.autosave, myfile.autosave1, myfile.autosave2...
#
## To use just add 'import nukescripts.autosave' in your init.py


def onAutoSave(filename):

  ## ignore untiled autosave
  if nuke.root().name() == 'Root':
    return filename

  fileNo = 0
  files = getAutoSaveFiles(filename)

  if len(files) > 0 :
    lastFile = files[-1]
    # get the last file number

    if len(lastFile) > 0:
      try:
        fileNo = int(lastFile[-1:])
      except:
        pass

      fileNo = fileNo + 1

  if ( fileNo > 9 ):
    fileNo = 0

  if ( fileNo != 0 ):
    filename = filename + str(fileNo)

  return filename


def onAutoSaveRestore(filename):

  files = getAutoSaveFiles(filename)

  if len(files) > 0:
    filename = files[-1]

  return filename

def onAutoSaveDelete(filename):

  ## only delete untiled autosave
  if nuke.root().name() == 'Root':
    return filename

  # return None here to not delete auto save file
  return None


def getAutoSaveFiles(filename):
  date_file_list = []
  files = glob.glob(filename + '[1-9]')
  files.extend( glob.glob(filename) )

  for file in files:
      # retrieves the stats for the current file as a tuple
      # (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
      # the tuple element mtime at index 8 is the last-modified-date
      stats = os.stat(file)
      # create tuple (year yyyy, month(1-12), day(1-31), hour(0-23), minute(0-59), second(0-59),
      # weekday(0-6, 0 is monday), Julian day(1-366), daylight flag(-1,0 or 1)) from seconds since epoch
      # note:  this tuple can be sorted properly by date and time
      lastmod_date = time.localtime(stats[8])
      #print image_file, lastmod_date   # test
      # create list of tuples ready for sorting by date
      date_file_tuple = lastmod_date, file
      date_file_list.append(date_file_tuple)

  date_file_list.sort()
  return [ filename for _, filename in date_file_list ]


nuke.addAutoSaveFilter( onAutoSave )
nuke.addAutoSaveRestoreFilter( onAutoSaveRestore )
nuke.addAutoSaveDeleteFilter( onAutoSaveDelete )

### As an example to remove the callbacks use this code
#nuke.removeAutoSaveFilter( onAutoSave )
#nuke.removeAutoSaveRestoreFilter( onAutoSaveRestore )
#nuke.removeAutoSaveDeleteFilter( onAutoSaveDelete )


在root上使用回调来添加双目设置

添加一段脚本,将新项目设置成stereo/multi view项目,操作如下:

# if necessary import the module that holds the script you want to run on startup:
import examples
# prepping the argument for this particular script
views = [('L', (0,.5,0)), ('R',(.5,0,0)), ('M',(.5,.5,0))]
# add script to the callback
nuke.addOnUserCreate( examples.setUpMultiView, views, nodeClass='Root' )

更多setupMultiView脚本,请看Setting Up Stereo

用户第一次创建某类节点时会触发onUserCreateOnCreate是每个节点创建时都会调用,甚至复制,从磁盘加载也会)。既然root也是个节点,就能用nodeClass过滤下保证仅root节点创建时执行。

上面的脚本加入menu.pyinit.py后,新的nuke脚本有三个view了,L(绿色),R(红色), M(黄色)。

image
image
默认色彩空间

Nuke10中可以动态替换Read和Write节点使用的色彩空间。

默认色彩空间是特定文件的Reader和Writer对象在读取和写入文件时告诉Nuke最可能的那个色彩空间。有以下几种途径来获取:

  • 文件metadata或文件头中存了colorspace的名字,Reader没准会用这个名字来设置默认色彩空间。

  • 文件包含特定数据类型,8bit,16bit,浮点数或者log空间,Nuke从项目设置中读取对应格式的色彩空间。

  • Reader、Writer 会通过ID请求内置的LUT ,例如REDlog, AlexaLog, ProTune,Gamma2.2( 查看DDImage/LUT.h 中的DataType)

多亏OpenColorIO(OCIO)在Nuke中的配置,现在很容易支持Nuke搞不定的色彩空间。由于色彩空间匹配不上,就很容易获取Nuke的错误状态,尤其使用自定义OCIO配置时。下面的回调能处理这些自定义配置导致的错误。

和其他回调一样,用下面的方法可以添加和删除回调:

nuke.addDefaultColorspaceMapper(function)
nuke.removeDefaultColorspaceMapper(function)

函数形式如下:

def myDefaultColorspaceMapper(colorspaceName, dataTypeHint) :
  # ... map colorspace name to desired colorspace ...
  return colorspaceName

传递的对象如下:

  • name -- Reader、Writer当前请求的色彩空间,可能是Nuke默认的,或其他存在,不存在的,没准不是色彩空间。
  • dataTypeHint -- 如上,为下面三种情况之一
    • nuke.INVALID - 仅设置了色彩空间的名字
    • 链接到项目设置里面的一个值, 如下-- 色彩空间会根据项目设置来定。注意,回调是链式调用,和项目设置中的色彩空间匹配这个不可靠,因为前面执行的回调可能改了它。
    • 内置LUT的ID -- 色彩空间名字为下表中的第一个。

项目设置中data type hint的值:

  • nuke.INT8 色彩空间会设置成项目设置中‘8-bit files’这个值
  • nuke.INT16 色彩空间会设置成项目设置中‘16-bit files’这个值
  • nuke.MONITOR 色彩空间会设置成项目设置中‘monitor’这个值, 用于显示,比如,viewers和 邮票
  • nuke.FLOAT 色彩空间会设置成项目设置中‘float files’这个值
  • nuke.LOG 色彩空间会设置成项目设置中‘log files’这个值

DDImage/LUT.h中的其他data-type,后向兼容Reader和Writer:

  • nuke.VIEWER - unused
  • 6 - equivalent to DD::Image::LUT::GAMMA1_8
  • 7 - equivalent to DD::Image::LUT::GAMMA2_2
  • 8 - equivalent to DD::Image::LUT::GAMMA2_4
  • 9 - equivalent to DD::Image::LUT::PANALOG
  • 10 - equivalent to DD::Image::LUT::REDLOG
  • 11 - equivalent to DD::Image::LUT::VIPERLOG
  • 12 - equivalent to DD::Image::LUT::ALEXAV3LOGC
  • 13 - equivalent to DD::Image::LUT::PLOGLIN
  • 14 - equivalent to DD::Image::LUT::SLOG
  • 15 - equivalent to DD::Image::LUT::SLOG1
  • 16 - equivalent to DD::Image::LUT::SLOG2
  • 17 - equivalent to DD::Image::LUT::SLOG3
  • 18 - equivalent to DD::Image::LUT::CLOG
  • 19 - equivalent to DD::Image::LUT::PROTUNE

例如: 想支持ACES 1.0.1 预览设置,添加如下回调函数来保证R3D文件的修正函数。

import nuke

def r3d_aces101_default_colorspace(name, dataTypeHint) :
  """ finds appropriate R3D colorspaces in the aces 101 config """

  usePrefs = (dataTypeHint >= 0) and (dataTypeHint <= nuke.FLOAT) #  5 = LUT::FLOAT
  if usePrefs:
    return name

  assert "/" not in name, "family name unexpectedly found in colorspace name"
  allColorspaces = nuke.colorspaces.getColorspaceList( nuke.thisNode().knob('colorspace') )[1:]
  exactMatch = name in allColorspaces
  if exactMatch:
    # if there's an exact name-match in the config use that
    return name

  nukeRedLog = (dataTypeHint == 10) # LUT::REDLOG
  if nukeRedLog:
    # first use the Nuke-side data-type hint
    aces101RedLog = "Input - RED - Curve - REDlogFilm"
    if aces101RedLog in allColorspaces :
      # we have the aces101 specific RED-log space, return that
      return aces101RedLog

  if name == "rec709":
    acesRec709 = "Output - Rec.709"
    if acesRec709 in allColorspaces:
      return acesRec709
  elif name == "REDSpace":
    acesRedSpace = "Input - RED - REDlogFilm - REDcolor4"
    if acesRedSpace in allColorspaces:
      return acesRedSpace


# add the mapper function to NUKE
nuke.addDefaultColorspaceMapper( r3d_aces101_default_colorspace )
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容